Configuration
One optional YAML file that tells every xcross command, and every IDE run, where your tools live.
No file, nothing changes. With a file, every xcross process, including IDE-driven runs, uses your declared tools and paths.
Where it lives
First hit wins:
- 1
$XCROSS_CONFIGHonored even if the file doesn't exist yet. - 2
<config dir>/config.yaml - 3
<config dir>/config.yml
| Host | Config dir |
|---|---|
| Windows | %APPDATA%\xcross |
| Linux / macOS | $XDG_CONFIG_HOME/xcross or ~/.config/xcross |
Commands
Roots │ Toolchains │ Tools │ Environment │ Setup │ Commands
──────────────────────────────────────────────────────────
> darwinSdk: /opt/xcross/sdk
flutterSdk: /opt/flutter
javaHome: /usr/lib/jvm/temurin-21
konanData: <unset>
[Save] [Validate] [Discard] [Quit]
Saves validate first and write atomically.
The file format
roots:
darwinSdk: "/opt/xcross/sdk"
flutterSdk: "/opt/flutter" # Flutter
javaHome: "/usr/lib/jvm/temurin-21" # Compose
konanData: "/var/cache/konan" # Compose
xcross: "/usr/local/bin/xcross"
toolchains:
swift: "/opt/swift/usr/bin"
llvm: ["/usr/lib/llvm-19/bin"]
tools:
flutter: "/opt/flutter/bin/flutter"
environment:
PATH: ["/opt/swift/usr/bin"]
setup: "https://example.com/setup.sh"
excluded_commands: ["update"]
Unknown keys are hard errors (Unknown key $.roots.flutter), so typos fail fast.
roots
| Key | Used for |
|---|---|
darwinSdk | Darwin SDK bundle |
flutterSdk | Flutter SDK, exported as FLUTTER_ROOT |
javaHome | JDK for Compose, exported as JAVA_HOME |
konanData | Kotlin/Native cache, exported as KONAN_DATA_DIR |
xcross | Launcher used when xcross or an IDE re-invokes itself |
toolchains, tools
toolchains.swift is one bin directory, toolchains.llvm one or many. They are searched before PATH. tools overrides single executables by name (Flutter.EXE = flutter), and each must exist and be executable.
tools.OpenAppleMacrosServer points xcross at your own build of the Apple macro server (SwiftUI, #Preview, FoundationModels) instead of the one bundled with the release.
environment
Allowlist only: PATH · CC · CXX · SWIFT_EXEC · SWIFT_EXEC_MANIFEST · JAVA_HOME · FLUTTER_ROOT · KONAN_DATA_DIR · LIBRARY_PATH · C_INCLUDE_PATH · CPLUS_INCLUDE_PATH. PATH entries are prepended.
setup
A local path or http(s) URL. Replaces the built-in xcross setup. Remote scripts are cached by SHA-256 and rechecked before each run.
excluded_commands
Hides commands from help, completion and dispatch. Handy for locked-down images.
Variables
$VAR, ${VAR} and ~ on POSIX, %VAR% on Windows. Recursive, cycle-checked, and undefined variables are errors.
roots:
flutterSdk: "$HOME/sdks/flutter"
What happens at startup
$XCROSS_CONFIG → config.yaml → config.ymlXCROSS_CONFIGRecipes
Complete files. Drop one in, then xcross config validate.
Pin a second LLVM without touching PATH
Your distro’s default lld is too old to link iOS, but a newer one sits beside it. Point
xcross at that one only, leaving the rest of the system alone.
toolchains:
llvm:
- "/usr/lib/llvm-19/bin"
Use a Flutter SDK that isn't installed globally
A checkout in your home directory, not on PATH. Set the root so xcross knows the SDK
layout, and the tool so it invokes the right binary.
roots:
flutterSdk: "$HOME/sdks/flutter"
tools:
flutter: "$HOME/sdks/flutter/bin/flutter"
Keep several Flutter versions side by side
One file per version, selected by an environment variable. Nothing is installed or symlinked, and switching is instant.
# ~/.config/xcross/flutter-3.29.yaml and flutter-3.35.yaml
XCROSS_CONFIG=~/.config/xcross/flutter-3.35.yaml xcross flutter run
Point your shell at one for the whole session:
export XCROSS_CONFIG=~/.config/xcross/flutter-3.35.yaml
Pin the whole toolchain explicitly
Nothing on PATH is trusted: every tool is named outright, so a system upgrade cannot
silently swap a compiler underneath your builds. update and setup are removed because
this host is provisioned elsewhere.
roots:
darwinSdk: "/opt/xcross/xcross-darwin.artifactbundle"
flutterSdk: "/opt/flutter"
xcross: "/usr/local/bin/xcross"
toolchains:
swift: "/opt/swift/usr/bin"
llvm:
- "/usr/lib/llvm-19/bin"
tools:
flutter: "/opt/flutter/bin/flutter"
swift: "/opt/swift/usr/bin/swift"
clang: "/usr/lib/llvm-19/bin/clang"
"clang++": "/usr/lib/llvm-19/bin/clang++"
"llvm-ar": "/usr/lib/llvm-19/bin/llvm-ar"
"ld64.lld": "/usr/lib/llvm-19/bin/ld64.lld"
excluded_commands:
- "update"
- "setup"
TipRun xcross config validate right after provisioning. It fails in a second on a bad
path, instead of thirty minutes into a build.
A managed developer fleet
Hosts provision themselves from a script you control, and the self-updater is removed so the
binary stays owned by your package manager. xcross setup runs the script and nothing else.
setup: "https://tools.example.com/xcross/setup.sh"
excluded_commands:
- "update"
Roll out a new script without running it, then let hosts pick it up on their next setup:
xcross setup --refresh-script
Swift toolchain outside the system prefix
A swiftly-managed or hand-unpacked toolchain. toolchains.swift makes tool lookup prefer
it, environment.PATH makes the child processes it spawns agree, and SWIFT_EXEC pins the
exact compiler binary used for manifest and build invocations.
toolchains:
swift: "$HOME/.swiftly/toolchains/6.4.0/usr/bin"
environment:
PATH:
- "$HOME/.swiftly/toolchains/6.4.0/usr/bin"
SWIFT_EXEC: "$HOME/.swiftly/toolchains/6.4.0/usr/bin/swift-frontend"
Compose Multiplatform on a shared machine
A JDK that isn’t the system default, and a Kotlin/Native cache on a fast or roomy volume
instead of ~/.konan. Both are exported to Gradle and the Kotlin compiler for you.
roots:
javaHome: "/usr/lib/jvm/temurin-21"
konanData: "/mnt/fast/konan"
Windows with a non-default install layout
Native %VAR% expansion, and Windows tool names normalize - .exe is stripped, so
flutter and flutter.exe are the same key.
roots:
flutterSdk: "C:\\src\\flutter"
darwinSdk: "%APPDATA%\\xcross\\swift-sdks\\xcross-darwin.artifactbundle"
tools:
flutter: "C:\\src\\flutter\\bin\\flutter.bat"
clang: "C:\\Program Files\\LLVM\\bin\\clang.exe"
Make an IDE match your terminal exactly
Editors spawn xcross with their own, usually thinner, environment. Setting the launcher root means the DAP server, hot reload, and the Apple tool shims all re-enter the same binary with the same configuration attached - so a run from the Debug button behaves identically to one from your shell.
roots:
xcross: "/usr/local/bin/xcross"
flutterSdk: "$HOME/sdks/flutter"
Try a change without committing to it
XCROSS_CONFIG is honored even when the file does not exist yet, so you can branch your
configuration, edit the copy in the TUI, and throw it away.
cp ~/.config/xcross/config.yaml /tmp/experiment.yaml
XCROSS_CONFIG=/tmp/experiment.yaml xcross config # edit the copy
XCROSS_CONFIG=/tmp/experiment.yaml xcross flutter build # try it
Troubleshooting
| Message | Cause |
|---|---|
No xcross configuration found. | show/validate ran in legacy mode. Create one with xcross config. |
Unknown key $.<path> | A typo or an unsupported key. Top-level keys are closed. |
Root <name> must be an absolute path | Relative paths are never accepted. |
Tool <name> is not executable | The override exists but lacks the executable bit or a Windows executable extension. |
Environment variable <NAME> is not allowlisted | Only the listed variables may be exported. |
Environment variable <NAME> is not defined | A $VAR / %VAR% reference could not be expanded. |
Environment expansion contains a cycle | Two values expand into each other. |
Interactive configuration requires a TTY. | Use xcross config show or validate in scripts. |