Conditional compilation
Include declarations and statements using immutable boolean build symbols.
Directives and expressions
#if XENON_WINDOWS && USE_VULKAN
private extern int InitializeWindowsVulkan();
#elif XENON_MACOS && USE_VULKAN
private extern int InitializeMoltenVK();
#else
private extern int InitializeFallback();
#endif
Use #if, zero or more #elif, an optional #else, and #endif. Only the first matching branch is included. Nested blocks work inside active and inactive branches. Directives can surround top-level declarations, members, or statements.
Conditions support identifiers, !, &&, ||, and parentheses. Precedence is !, then &&, then ||. An undefined identifier is false. Symbols are case-sensitive.
A directive starts with # as the first non-whitespace character on a physical line. Apparent directives inside strings or comments are ignored. A trailing // comment is permitted. Inactive ordinary source is excluded before parsing and binding; directive structure and expressions are still checked. Original source locations and line endings are preserved in diagnostics.
There are no text macros, numeric/string values, substitutions, #define, or #undef. Orphan directives, missing expressions, malformed conditions and unterminated blocks are errors.
Project and command-line symbols
[build]
defines = ["USE_VULKAN", "ENABLE_DIAGNOSTICS"]
xenon build Engine.xeproj -DUSE_VULKAN -DENABLE_VALIDATION
xenon build Engine.xeproj --define USE_VULKAN
xenon main.xe --define=ENABLE_VALIDATION
-DNAME, --define NAME and --define=NAME are equivalent. Names use Xenon's identifier character rules. Empty names, 123ABC, USE-VULKAN, whitespace and -DVALUE=123 are rejected. The entire XENON_ prefix is compiler-reserved and cannot be supplied through projects, CLI or the public defines API.
The active set is the union of target built-ins, profile built-ins, this project's defines and invocation defines. Duplicates collapse. Project defines remain local to their project; CLI defines reach every project compiled in the invocation, including dependencies.
Compiler built-ins
| Selected target/profile | Symbols |
|---|---|
| Windows | XENON_WINDOWS |
| Linux | XENON_LINUX, XENON_UNIX |
| macOS | XENON_MACOS, XENON_UNIX |
| x86-64 | XENON_X64 |
| AArch64 / Apple Silicon | XENON_ARM64 |
| Existing 32-bit x86 target | XENON_X86 |
| debug / release profile | XENON_DEBUG / XENON_RELEASE |
Each listed OS/architecture selects only its matching primary symbol. Other LLVM targets receive no invented OS/architecture symbol. Profiles select exactly one profile symbol based on the profile name, independently of optimization and debug-info settings.
Built-ins describe the compilation target. For example, --target aarch64-apple-darwin selects macOS, Unix and ARM64 even when the compiler runs on Windows x64. With no explicit target, compiler and editor share a resolver based on the current process architecture and OS: <arch>-pc-windows-msvc, <arch>-unknown-linux-gnu, or <arch>-apple-darwin. Specify an explicit target when a different ABI is required.
XELIB build configuration
A .xelib contains the already-bound program for one concrete target ABI. Target/architecture symbols and all user/project defines are resolved during library production. Inactive symbols and externs do not enter the artifact. Consumer defines cannot reselect library code, and original sources are not needed.
Active semantic errors always prevent library emission. Errors confined to excluded branches remain ignored. The producer binds target-dependent constants and layout; open generic layout expressions are resolved after specialization within the same ABI.
XELIB source directives cannot use XENON_DEBUG or XENON_RELEASE. Use an explicit feature define for semantic differences. A single XELIB can then serve Debug/Release and O0/O3 consumers on its target. Other source project types retain profile built-ins.
The canonical triple and sorted producer defines are stored inside the artifact and covered by its content identity. Consumers reject incompatible targets before binding. Output directories separate targets and define sets; changing flag order does not create a different artifact. See XELIB libraries for paths and dependencies.
STD is built separately for each supported ABI. Its clock branches and private platform constants use ordinary source #if. No compiler-known target query functions are required. Container / Library IR / language versions are 1 / 1 / 1.
Compiler API, editor and rebuilds
var conditional = new ConditionalCompilationOptions(
defines: ["ENABLE_VALIDATION"], // global build symbols
projectDefines: ["LIBRARY_PRIVATE_FEATURE"],
targetTriple: "aarch64-apple-darwin",
profile: "release");
var options = new CompilationOptions(ConditionalCompilation: conditional);
Options capture a sorted immutable set; caller collection mutations do not change an existing compilation. Changing conditional options reparses the compilation's own source and checks the targets of referenced XELIBs. Low-level target-independent parsing requires an explicit target when target built-ins are referenced; native emission resolves its requested target before checking source diagnostics.
LSP snapshots use project defines and the workspace target, selected with initializationOptions.targetTriple or the shared default resolver. The target survives edits and project reloads. Changing conditional environments invalidates semantic and symbol/reference indexes. The build driver recompiles source projects for each invocation; target and sorted defines also participate in fingerprints and output paths.
Pass a loaded library to Compilation.Create to check compatibility and bind the consumer. XelibReader.Read directly exposes the library's selected symbols; no materialization API or full library rebinding is needed. A SourceCompilationReference also freezes its snapshot's effective target; set the target before creating a source reference for cross-compilation. Low-level producers must bind target layout before writing a library that requires it.