- name
- bentley-build
- description
- Expert reference for the BentleyBuild (`bb`) system used in the imodel-native codebase. Use when asked to build, compile, or run `bb` commands; when editing `.PartFile.xml`, `.mke`, or `.mki` files; when working with build strategies, part names, or architectures; or when diagnosing build failures.
# Bentley Build System (BentleyBuild / bb)
**`bb`** = `python src/BentleyBuild/BentleyBuild.py`
Set required environment variables, then invoke Python directly.
---
## Environment Setup
### Key environment variables
| Variable | Value | Notes |
|----------|-------|-------|
| `SrcRoot` | `<workspace>/src/` | Trailing slash required |
| `OutRoot` | `<workspace>/out/debug/` (Unix) · `<workspace>\outd\` (Win) | |
| `BuildStrategy` | `iModelConsole` | See strategies below |
| `BuildArchitecture` | `macosarm64` / `macosx64` (Mac) · `x64` (Win) · `linuxx64` (Linux) · `androidarm64` / `androidx64` (Android) · `iosarm64` (iOS) | |
| `BB_PRIMARY_REPO` | `imodel-native-internal` | |
| `DEBUG` | `1` | Debug build; use `NDEBUG=1` for release |
| `BSI` | `1` | **Required.** Enables `InternalPlatformSetup.mki` which defines `sharedMki`, `BuildContextPublicApiDir`, `LinkFirstDepToFirstTarget`. Without it builds silently misfire or fail with `can't open include file linkLibrary.mki`. |
| `imodeljsDir` | `<workspace>/../itwinjs-core` | Addon builds only |
### Quick setup (Unix/macOS)
```bash
export SrcRoot="<workspace>/src/"
export OutRoot="<workspace>/out/debug/"
export BuildStrategy="iModelConsole"
export BuildArchitecture="macosarm64" # or macosx64, linuxx64, androidarm64, androidx64, iosarm64
export BB_PRIMARY_REPO="imodel-native-internal"
export DEBUG="1" # use NDEBUG=1 for release
export BSI="1"
```
### Quick setup (Windows PowerShell)
```powershell
$env:SrcRoot = "<workspace>\src\"; $env:OutRoot = "<workspace>\outd\"
$env:BuildStrategy = "iModelConsole"; $env:BuildArchitecture = "x64"
$env:BB_PRIMARY_REPO = "imodel-native-internal"; $env:DEBUG = "1"; $env:BSI = "1"
```
Default toolset is **`WINDOWS_CLANG`**. If LLVM is not installed, add `<DefaultToolset Platform="x64" Toolset="VS2022"/>` to `iModelBase.BuildStrategy.xml`.
---
## bb Command Reference
```
bb [flags] <action>
Flags (must precede action):
-r REPO repository of the starting PartFile
-f PARTFILE path to PartFile (relative to repo root, no .PartFile.xml extension)
-p PARTNAME part name (or "PartFile:PartName" combined)
-s STRATEGY override BuildStrategy (combine with ; and +)
-a PLATFORM override BuildArchitecture (combine with +)
-n skip SubParts — build only the named part
-N THREADS number of parallel threads
-v LEVEL verbosity 0–6 (default 3)
Actions:
build incremental build
rebuild force rebuild (ignores up-to-date checks)
clean alias for rebuild -c
pull / get pull all repos
push push all repos
status VCS status summary
incoming repos with unpulled changes
outgoing repos with unpushed changes
git <cmd> run git command across all repos
debug inspect resolved strategy / part graph
troubleshoot detect common BB/VCS issues
tools print recommended tool versions
```
> ⚠️ **Flags must precede the action:**
> ```bash
> bb -r imodel-native -f iModelCore/ECDb/ECDb -p ECDbLibrary build # ✓
> bb build -f iModelCore/ECDb/ECDb # ✗
> ```
---
## Build Strategies
### Strategy chain
```
iModelConsole.BuildStrategy.xml
└── iModelBase.BuildStrategy.xml ← toolset overrides live here
└── Base.BuildStrategy.xml ← default toolsets, LKG/NuGet sources
```
### Named strategies
| Alias | Purpose |
|-------|---------|
| `iModelNative` | **CI default** — builds and also runs unit tests |
| `iModelConsole` | iModelCore + iModelPlatform + iModelConsoleExe (no tests) |
| `iModelCore` | Core libraries only |
| `iModelJsNodeAddon` | Node.js `.node` addon (**requires `-s` explicitly**) |
| `iModelJsNodeAddon.Dev` | Dev/debug variant of addon |
| `ECObjects` | EC object system only |
| `BuildAll` | Force everything from source |
**Strategy files:** `src/imodel-native-internal/build/strategies/`
```xml
<!-- Build a part from source instead of LKG -->
<PartStrategy PartFile="ECDb" PartName="*" BuildFromSource="Once"/>
```
---
## Top-Level Build Commands
| Product | `-r` | `-f` | `-p` | `-s` needed? |
|---------|------|------|------|--------------|
| ECDb | `imodel-native` | `iModelCore/ECDb/ECDb` | `ECDbLibrary` | No |
| iModelCore | `imodel-native` | `iModelCore/iModelCore` | `iModelCore` | No |
| iModelConsole | `imodel-native` | `iModelCore/iModelPlatform/iModelPlatform` | `iModelConsole` | No |
| iModelJsNodeAddon | `imodel-native` | `iModelJsNodeAddon/iModelJsNodeAddon` | `iModelJsNodeAddonPRG` | **Yes** ⚠️ |
> ⚠️ **iModelJsNodeAddon** must pass `-s "imodel-native-internal:build/strategies/iModelJsNodeAddon"`.
> **Never guess part names.** Always verify: `grep 'Part Name=' src/imodel-native/<path>.PartFile.xml`
```bash
bb -r imodel-native -f iModelCore/iModelCore -p iModelCore build
bb -r imodel-native -f iModelCore/iModelPlatform/iModelPlatform -p iModelConsole build
bb -r imodel-native -f iModelCore/ECDb/ECDb -p ECDbLibrary build
bb -n -r imodel-native -f iModelCore/ECDb/ECDb -p ECDbLibrary build # skip SubParts
bb -r imodel-native -f iModelCore/ECDb/ECDb -p ECDbLibrary rebuild # force rebuild
bb -a "macosarm64+macosx64" build # universal macOS
# iModelJsNodeAddon — strategy required
bb -s "imodel-native-internal:build/strategies/iModelJsNodeAddon" \
-r imodel-native -f iModelJsNodeAddon/iModelJsNodeAddon -p iModelJsNodeAddonPRG build
```
---
## Part Files (.PartFile.xml)
### Key PartFile locations (relative to `src/imodel-native/`)
| Module | Path |
|--------|------|
| iModelCore | `iModelCore/iModelCore.PartFile.xml` |
| iModelPlatform | `iModelCore/iModelPlatform/iModelPlatform.PartFile.xml` |
| ECDb | `iModelCore/ECDb/ECDb.PartFile.xml` |
| ECObjects | `iModelCore/ecobjects/ECObjects.PartFile.xml` |
| BeSQLite | `iModelCore/BeSQLite/BeSQLite.PartFile.xml` |
| GeomLibs | `iModelCore/GeomLibs/geomlibs.PartFile.xml` |
| iModelJsNodeAddon | `iModelJsNodeAddon/iModelJsNodeAddon.PartFile.xml` |
**`imodel-native-internal`** adds: `PSBRepGeometry`, `BRepCore`, `Visualization`, `iModelJsMobile`.
### Structure
```xml
<BuildContext>
<!-- Compilable unit -->
<Part Name="ECDbLibrary" BentleyBuildMakeFile="ECDb/ECDb.mke"
OnlyPlatforms="x64,macos*,linux*">
<SubPart PartName="BeSQLite" PartFile="iModelCore/BeSQLite/BeSQLite"/>
<SubPart PartName="ECObjects" PartFile="iModelCore/ecobjects/ECObjects"/>
<!-- Cross-repo SubPart -->
<SubPart PartName="PSBRepGeometry"
PartFile="iModelCore/PSBRepGeometry/PSBRepGeometry"
Repository="imodel-native-internal"/>
<Bindings>
<Libs>Delivery/$(libprefix)iTwinSQLiteEC$(libext)</Libs>
</Bindings>
</Part>
<!-- Composite — no .mke, aggregates SubParts -->
<Part Name="iModelConsole">
<SubPart PartName="iModelPlatformDLL"/>
<SubPart PartName="iModelConsoleExe" DeferType="BuildSamples"/>
</Part>
</BuildContext>
```
### Key attributes
| Attribute | Meaning |
|-----------|---------|
| `BentleyBuildMakeFile` | Path to `.mke` (relative to PartFile dir) |
| `OnlyPlatforms` / `ExcludePlatforms` | Platform filters (supports `*` glob) |
| `DeferType` | `BuildUnitTests` / `RunUnitTests` / `BuildSamples` |
| `LibType` on `<SubPart>` | `Static` / `Dynamic` — forces lib type |
| `Repository` on `<SubPart>` | Cross-repo dependency |
---
## Make Files (.mke / .mki)
Every `.mke` starts with `%include mdl.mki` (at `src/bsicommon/PublicSDK/mdl.mki`).
`mdl.mki` selects based on `BSI`: `BSI=1` → `InternalPlatformSetup.mki` (defines `sharedMki`, `BuildContextPublicApiDir`, etc.); unset → `PlatformSetup.mki` (**many vars undefined**).
### Key predefined variables
| Variable | Value |
|----------|-------|
| `TARGET_PLATFORM` | `Windows` \| `MacOS` \| `Linux` \| `iOS` \| `Android` |
| `TARGET_PROCESSOR_ARCHITECTURE` | `x64` \| `arm64` \| `AndroidARM64` \| `iOSARM64` |
| `SrcRoot` | source root (trailing slash) |
| `OutputRootDir` | `$(OutRoot)$(arch)/` |
| `sharedMki` | `$(SrcRoot)bsicommon/sharedmki/` |
| `libprefix` | `lib` (Unix) · empty (Windows) |
| `libext` | link library extension: typically `.lib` (Windows), `.a` (most Unix), **may be `.dylib` on MacOS when linking shared libs**. Use `$(stlibext)` for “always static” archives. |
| `shlibext` | shared-library filename extension: `.dylib` / `.so` / `.dll`. Use for naming shared objects, not for abstract link names (use `$(libext)` there). |
| `stlibext` | static library extension (always `.a` / `.lib`), even when `$(libext)` resolves to a shared-lib extension such as `.dylib` on MacOS. |
### .mke syntax
```makefile
%include mdl.mki # always first
appName = mylib
BUILD_WITH_C20 = 1
cDefs + -DMY_DEFINE=1 # append to compiler defs
%if $(TARGET_PLATFORM) == "MacOS"
cDefs + -DMACOS
%elif $(TARGET_PLATFORM) == "Windows"
cDefs + -DWIN32
%endif
%if defined(SOME_FEATURE)
cDefs + -DSOME_FEATURE=1
%endif
o = $(OutputRootDir)Build/mylib/
```
### BBMake parser gotcha (`always:` blocks)
- The first line after `always:` must be an executable command.
- Do **not** place a comment immediately after `always:`.
- If you need a comment, place it before `always:` or after at least one command line.
- Violating this can produce parser/build errors such as:
- `BBMake: no rules to build always`
- `error : not rule or dependency`
Example (bad):
```makefile
always:
# This comment can trigger a parse error in BBMake
$(SomeCommand)
```
Example (good):
```makefile
# Build step description
always:
$(SomeCommand)
# Optional comment is safe after a command line
```
---
## Dependency Trees (Simplified)
### iModelConsole
```
iModelConsole
└── iModelPlatformDLL
├── ECDbLibrary → BeSQLite, ECObjects, GeomLibs, BeJsonCpp, google_re2
├── ECPresentation → ECDb, ECObjects
├── GeoCoord
├── Bentley → BeIcu4cLibrary, BeOpenSSL, BeLibxml2, BeCurl
└── GeomDlls, Freetype2, PNG, Zlib, proxyres, folly, napi-lib
```
### iModelJsNodeAddon
```
iModelJsNodeAddonPRG
└── iModelJsNative-Dynamic (.node file)
└── [all iModelPlatformDLL deps above]
├── PSBRepGeometry (imodel-native-internal)
├── BRepCore (imodel-native-internal)
└── VisualizationDll (imodel-native-internal)
```
---
## Troubleshooting
| Symptom | Fix |
|---------|-----|
| `BBMake: no rules to build always` or `error : not rule or dependency` near an `always:` block | Ensure the first line after `always:` is a command (not a comment). Keep `%if/%else/%endif` directives at column 1. |
| `BSI=1` not set | Set `BSI=1`. Without it `sharedMki`, `BuildContextPublicApiDir`, `LinkFirstDepToFirstTarget` are undefined — silent failures or `can't open include file linkLibrary.mki`. |
| Static binding file missing after broken run | Prior run without `BSI=1` left stale `.log` files in `outd\<arch>\static\BuildContexts\<Part>\Logs\`. Delete them and re-run with `BSI=1`. |
Ver en GitHub