The native C++ engine powering the iTwin.js platform by Bentley Systems.
This repository contains the C++ source for the @bentley/imodeljs-native npm package — a Node.js native addon that provides iModel file I/O, ECSQL querying, change tracking, geometry processing, and more to the @itwin/core-backend TypeScript layer.
graph TD
TS["@itwin/core-backend · (TypeScript)"]
subgraph addon ["iModelJsNodeAddon"]
TSAPI["NativeLibrary.ts"]
NAPI["N-API Addon · IModelJsNative.cpp · JsInterop.cpp"]
end
subgraph core ["iModelCore (C++)"]
IP["iModelPlatform · Elements · Models · BIS schemas · TxnManager"]
ECP["ECPresentation · Presentation Rules Engine"]
ECDB["ECDb · ECSSQL Engine · Object-Relational Mapping"]
ECO["ECObjects · EC Schema Metadata"]
GEO["GeomLibs · GeoCoord · Units"]
BESQL["BeSQLite · SQLite Wrapper · Changesets · Cloud Sync"]
end
SQLITE[("SQLite · .bim iModel files")]
TS --> TSAPI --> NAPI
NAPI --> IP
NAPI --> ECP
NAPI --> ECDB
IP --> ECDB
IP --> GEO
ECP --> ECDB
ECDB --> ECO
ECDB --> BESQL
BESQL --> SQLITE
| Module | Location | Description |
|---|---|---|
| BeSQLite | iModelCore/BeSQLite/ |
Low-level SQLite wrapper. Handles changeset generation, cloud SQLite sync, and the briefcase-based ID sequence. |
| ECObjects | iModelCore/ecobjects/ |
EC schema system — defines the metadata model: schemas, classes, properties, relationships, and mixins. |
| ECDb | iModelCore/ECDb/ |
Object-relational database engine built on BeSQLite and ECObjects. Implements ECSQL, a SQL dialect that maps EC class/property names to physical SQLite tables. |
| iModelPlatform | iModelCore/iModelPlatform/ |
Business logic layer. Implements BIS schemas, Elements, Models, Views, Fonts, and TxnManager (changeset tracking). |
| ECPresentation | iModelCore/ECPresentation/ |
Presentation rules engine used by iTwin.js to drive hierarchy trees and property grids in UI. |
| GeomLibs | iModelCore/GeomLibs/ |
3D geometry math: curves, surfaces, solids, polyfaces, and more. |
| GeoCoord | iModelCore/GeoCoord/ |
Geospatial coordinate reference system (CRS) conversion. |
| Units | iModelCore/Units/ |
Unit conversion and formatting. |
| Node Addon | iModelJsNodeAddon/ |
N-API glue layer. Wraps all of the above and exposes them to Node.js. |
| TypeScript API | iModelJsNodeAddon/api_package/ts/ |
TypeScript declarations (NativeLibrary.ts) that form the public contract consumed by @itwin/core-backend. |
imodel-native/
├── iModelCore/ # C++ core libraries
│ ├── BeSQLite/ # SQLite wrapper
│ ├── Bentley/ # Core utilities
│ ├── ecobjects/ # EC schema system
│ ├── ECDb/ # ECSQL engine & object store
│ ├── ECPresentation/ # Presentation rules engine
│ ├── GeoCoord/ # Geospatial CRS
│ ├── GeomLibs/ # 3D geometry
│ ├── iModelPlatform/ # BIS platform layer
│ ├── Units/ # Unit conversion
│ └── libsrc/ # Third-party vendored libraries
├── iModelJsNodeAddon/ # N-API addon + TypeScript API
│ ├── api_package/ts/ # TypeScript declarations
│ ├── IModelJsNative.cpp # Main N-API bindings
│ └── JsInterop.cpp # Helper interop functions
└── schemas/ # BIS schema definitions
The build is driven by BentleyBuild (bb), an orchestrator that resolves parts across repositories. imodel-native does not build on its own - it needs imodel-native-internal (build strategies, third-party sources) checked out beside it, plus the toolchain. Getting to that first successful build is covered by the bootstrap guide on the internal wiki; this section assumes you are past it.
bb reads its configuration from environment variables. The bootstrap creates env.sh / env.bat for this. Running in a shell exposes these variables:
| Variable | What it selects |
|---|---|
SrcRoot |
the directory holding the checked-out repositories |
OutRoot |
where build output goes |
BuildStrategy |
which parts get built. iModelCore for the C++ libraries and their tests; iModelCore+iModelJsNodeAddon.Dev to also produce the Node addon |
BuildArchitecture |
LinuxX64, Winx64, MacOSARM64, the Android and iOS variants - bb -h lists them all |
BB_PRIMARY_REPO |
imodel-native-internal |
bb pull # update every repository in the strategy
bb build # incremental build of everything the strategy names
bb build --tmrbuild --noprompt # delete the whole output tree first, then build
bb rebuild ECObjects:*test* # force a rebuild of matching parts only--tmr deletes the output tree without building; --tmrbuild deletes and then builds. --noprompt skips the confirmation, which is what makes either usable from a script.
Narrow a build to one product with -p (product), -s (strategy) and -f (part file, without the .PartFile.xml suffix):
bb -p ECDb-Gtest -s iModelCore -f iModelCore/ECDb/ECDb build
bb -p Units-Gtest -s iModelCore -f iModelCore/Units/Units buildOutput lands in $OutRoot/<BuildArchitecture>/Product/<Product>/.
Each module owns a GoogleTest executable, built as its own product.
| Product | Executable | Tests live in |
|---|---|---|
Bentley-Gtest |
BentleyTest |
iModelCore/Bentley/Tests/ |
BeSQLite-Gtest |
BeSQLiteTest |
iModelCore/BeSQLite/Tests/ |
ECObjects-Gtest |
ECObjectsTest |
iModelCore/ecobjects/test/ |
ECDb-Gtest |
ECDbTest |
iModelCore/ECDb/Tests/ |
GeomLibs-Gtest |
GeomLibsTest |
iModelCore/GeomLibs/geom/test/ |
GeoCoord-GTest |
GeoCoordTests |
iModelCore/GeoCoord/Tests/ |
Units-Gtest |
UnitsTest |
iModelCore/Units/tests/ |
ECPresentation-Gtest |
ECPresentationTest |
iModelCore/ECPresentation/Tests/ |
iModelPlatform-Gtest |
iModelPlatformTest |
iModelCore/iModelPlatform/Tests/DgnProject/ |
There are matching -Performance products for ECDb, ECObjects and iModelPlatform, a GeoCoord-ExtensiveGTest, and an ECPresentation stress product. None of them run in a normal build.
Build one and run it:
bb -p ECDb-Gtest -s iModelCore -f iModelCore/ECDb/ECDb build
$OutRoot/LinuxX64/Product/ECDb-Gtest/ECDbTest
$OutRoot/LinuxX64/Product/ECDb-Gtest/ECDbTest --gtest_filter=SchemaSyncImportTestFixture.*Each suite has an ignore_list.txt next to its tests (iModelCore/ECDb/Tests/ignore_list.txt and friends), delivered to the test executable's Assets/Ignore. Anything named there is skipped by a plain run, with a comment saying why.
An explicit --gtest_filter on the command line replaces the ignore list entirely. That is how you run something the list excludes, and it is also why a filtered run is not the same set of tests as an unfiltered one.
The positive counterpart, Assets/Run, cannot be combined with Assets/Ignore - BeGTestExe.cpp builds the ignore filter and then assigns the run filter over it, discarding the first. Use Ignore only.
Some areas are worth testing far more thoroughly than is affordable on every build. Those suites are split in two, selected by fixture name and gated on an environment variable:
| Tier | Runs | How |
|---|---|---|
| core | every build | a plain run of the executable |
| extended | on demand, and on a schedule | set IMODEL_RUN_EXTENDED_TESTS=1 |
A fixture whose name ends in ExtendedTests calls GTEST_SKIP() from its SetUp unless the variable is set to 1, true or yes. So an ordinary build reports those tests as skipped rather than running them, a developer exports the variable to run everything, and a build machine sets it on the job. Nothing in the build scripts or the pipeline changes either way, and the skipped tests stay visible in the log.
ECDbTestFixture::ExtendedTestsEnabled() reads the variable. Narrow a run further with --gtest_filter=*ExtendedTests.* if you want the extended tier on its own.
ECDb uses this for schema sync, where the permutations of concurrent schema changes across briefcases are effectively unbounded.
A test belongs in the extended tier when it covers one more permutation of behaviour the core tier already covers. The behaviour itself stays in the core tier, however slow it is.
IModelEvolutionTests (iModelCore/iModelPlatform/Tests/DgnProject/Compatibility/) checks that today's code still reads files written by older versions. It is deliberately outside the normal build graph - it pulls historical test runners and files from an internal NuGet feed and runs every runner against every file. It has its own build strategy, iModelEvolutionTests, and no scheduled pipeline.
iModelJsNodeAddon/ wraps the C++ libraries as the @bentley/imodeljs-native npm package. Building it needs a strategy that includes it, such as iModelCore+iModelJsNodeAddon.Dev.
The contract with the TypeScript side is iModelJsNodeAddon/api_package/ts/src/NativeLibrary.ts. A change to the C++ surface is not usable from @itwin/core-backend until it is declared there, so the order is: change the C++, declare it in NativeLibrary.ts, then write the wrapper in core/backend.
To test a change against a local itwinjs-core checkout, install the freshly built addon over the published one:
iModelJsNodeAddon/installnativeplatformLinux.sh /path/to/itwinjs-coreinstallnativeplatform.bat and installnativeplatformMacOS.sh are the equivalents.
The addon has its own TypeScript tests, which exercise paths the C++ suites structurally cannot reach - anything that needs a real DgnDb plus the JavaScript layer:
cd iModelJsNodeAddon/api_package/ts
npm run build && npm run pretest
npm testThe imodel-native backend detects the system proxy configuration on Windows, macOS, and iOS, and it automatically uses this when doing SQLite downloads from remote servers. This includes support for Proxy Auto-Config (PAC) scripts. None of this is supported on the Linux and Android backends.
- iTwin.js — The TypeScript platform that consumes this native package
- iTwin Platform Docs — Official documentation
Apache License 2.0 — Copyright © Bentley Systems, Incorporated. All rights reserved.