Project files (.xeproj)
A .xeproj file describes a Xenon compilation: which source tree to include, what to build, which profile to use and which Xenon or native libraries to consume.
Why a project file exists
A .xe file contains language declarations. A .xeproj file contains build configuration. It gives the compiler one entry point for a program or library spread across many source files, so you do not have to pass each source path separately.
The project file is UTF-8 text using a TOML-style configuration format with the settings shown on this page. Settings belong to sections such as [project] and [source]; strings use double quotes, and # starts a comment outside a string.
The project determines which files enter the compilation. A using directive resolves names among declarations already included in that compilation. It does not load a file, search a directory, download a package or link a library.
A project with two source files
Create the following layout. The source directory exists beside the project file:
ExampleApp/
App.xeproj
src/
Main.xe
Math/
Arithmetic.xe
App.xeproj:
[project]
name = "ExampleApp"
type = "executable"
[source]
root = "src"
src/Math/Arithmetic.xe:
namespace Example.Math;
public int Add(int a, int b)
{
return a + b;
}
src/Main.xe:
using Example.Math;
namespace Example;
int Main()
{
int result = Add(20, 22);
if (result == 42)
return 0;
return 1;
}
With the Xenon CLI on your path, run these commands from ExampleApp:
xenon build App.xeproj
xenon run App.xeproj
The executable returns exit code 0 after confirming that the call produced 42.
How the project makes using possible
- The loader resolves
source.rootrelative to the directory containingApp.xeprojand recursively collects the.xefiles below it. - All discovered files are compiled together, so their declared namespaces and types are available throughout the project.
- Each file's
usingdirectives apply to that file. In the example,Example.Mathis available becauseArithmetic.xebelongs to the project. - In
Main.xe,Addis found through the imported namespace. The function ispublic, so code inExamplecan call it.
Folder names do not declare namespaces. The folder Math/ could be renamed without changing namespace Example.Math;. Likewise, the project name ExampleApp does not automatically create an ExampleApp namespace.
Imports affect only their own file and do not bypass access modifiers. You can also call Example.Math.Add(20, 22) without a using, but Arithmetic.xe must still be included in the compilation. See namespaces and aliases and access modifiers.
xenon build src/Main.xe selects only that file. It does not search for a nearby .xeproj or automatically include Arithmetic.xe. In this example it reports an unknown namespace. Build App.xeproj or its directory to include the complete source tree.
Explicit and implicit projects
A project file is useful, but not mandatory for every compilation. The input passed to xenon build or xenon run determines how the compiler selects sources:
| Input | Selection rule |
|---|---|
App.xeproj | Load that project's settings and source root. |
A directory with one .xeproj directly inside it | Load that project. Only its configured source tree is included. |
A directory with no .xeproj directly inside it | Create an implicit executable project from all .xe files recursively below the directory. The directory name becomes the project name. |
A directory with several .xeproj files directly inside it | Report an ambiguity. Pass the intended project file explicitly. |
One .xe file | Create an implicit executable project containing only that file. The filename without its extension becomes the project name. |
| No path | Resolve the current working directory using the directory rules above. |
For example, xenon build . inside ExampleApp selects App.xeproj. An implicit directory project can also use using across its included files; the directive does not require a physical project file.
Source discovery includes every .xe file under the selected source root. Paths are sorted with case-insensitive ordinal comparison on Windows and ordinal comparison on other supported platforms. Keep unrelated examples, test programs with their own Main, and generated .xe files outside that root.
Project and source settings
| Setting | Required / default | Meaning |
|---|---|---|
[project] name | Required, nonempty quoted string | Project identity used for output filenames. It does not have to match the .xeproj filename. |
[project] type | Required | "executable", "static-library", "shared-library" or target-specific "xenon-library". |
[project] version | Optional quoted string | Stored project metadata. It does not select a language version or resolve dependencies. |
[source] root | "src" | Directory searched recursively for source files. A relative path is based on the project file's directory. |
[references] projects | [] | Array of referenced Xenon library project files. Relative paths are based on the current project file's directory. |
[libraries] libraries | [] | Explicit .xelib paths and native library inputs. Relative XELIB paths are based on the project file's directory. |
[libraries] library-paths | [] | Native linker search directories. These paths are not searched for XELIB files. |
The source root must already exist and contain at least one .xe file. Omitting [source] uses src; it does not switch to the implicit project's whole-directory search. For paths in quoted strings, forward slashes work without escaping, including on Windows.
An executable requires exactly one namespace-level int Main(); the compiler generates the native entry wrapper. Static and shared libraries do not require that entry point. xenon run accepts executable projects only.
Native dependencies are a separate mechanism from using. For example, this configuration tells the linker where to find a native library; it does not import a Xenon namespace or read the library's declarations:
[libraries]
library-paths = ["native/lib"]
libraries = ["math"]
Declare the native functions with extern. See native dependencies and building libraries for platform naming, exports and runtime library search. Static archives do not bundle their native dependencies; those must be supplied when the consumer is linked.
Entries ending in .xelib are target-specific Xenon semantic libraries rather than native linker inputs. The consumer requires the exact canonical target triple recorded by the library and reads its public symbols and implementation bodies without needing the producer's source files. Producer defines are already fixed in the artifact and cannot be reselected by consumer defines. See XELIB libraries for build output, explicit-reference and dependency rules.
Project references
Use [references] projects when one Xenon project depends on another Xenon library project:
[references]
projects = [
"../SomeLibrary/SomeLibrary.xeproj"
]
Each relative entry is resolved from the directory containing the project file that declares it. Referenced projects are loaded recursively and built before the project that uses them, with the same selected profile and target. Their public declarations participate in the dependent compilation, while normal namespace, using and access rules still apply. Their generated library artifacts are supplied when the dependent native artifact is linked. For xenon-library projects, the transitive XELIB dependency closure follows the project graph automatically and duplicate content identities are loaded once.
References must name existing .xeproj library projects. Executable projects cannot be dependencies. Duplicate references, dependency cycles, missing projects and duplicate project names in one graph are reported as project-system errors. Shared-library dependencies must still be available to the platform loader at runtime.
Debug and release profiles
Two build profiles are available. These are their default values; add the sections only when you want to make the defaults explicit or override individual settings:
[profile.debug]
optimization = 0
debug-info = true
checks = true
[profile.release]
optimization = 3
debug-info = false
checks = false
The default is debug. Select release with --release or --profile release; select debug explicitly with --profile debug. Missing settings inherit the defaults of their own profile, not values from the other profile.
optimization accepts an integer from 0 through 3. debug-info and checks accept booleans.
Build commands and output
From the example project's directory:
# Build using the default debug profile
xenon build App.xeproj
# Build and run the executable
xenon run App.xeproj
# Release project build
xenon build App.xeproj --release
# Emit an object for a non-host target
xenon build App.xeproj --target aarch64-unknown-linux-gnu
# Direct source mode: explicitly write LLVM IR beside Main.xe
xenon src/Main.xe --emit-llvm
A build loads the project, checks all selected sources, emits an object and invokes the host linker or archiver for the requested project type. run builds first and returns the executable's exit code. For a non-host target, the build produces an object file to pass to a target-specific SDK and linker.
| Output | Path relative to the project directory |
|---|---|
| Intermediate object | .xenon/obj/<profile>/<target-triple>/ExampleApp.obj on Windows; .o on other targets. |
| Native executable or library | build/<profile>/<target-triple>/, with a filename based on project.name, project type and platform. |
| Target-specific Xenon library | build/<canonical-target-triple>/<project-name>.xelib. When user or project defines are active, the path is build/<canonical-target-triple>/defines-<fingerprint>/<project-name>.xelib. |
| LLVM IR intermediate | .xenon/obj/<profile>/<target-triple>/ExampleApp.ll, alongside the intermediate object. |
The XELIB define fingerprint is the lowercase SHA-256 digest of the sorted, deduplicated user and project define names. Target and effective defines identify the semantic artifact; the selected Debug/Release profile does not. A XELIB can therefore be reused by Debug and Release consumers when its canonical target triple and fixed define set match.
The project build and run pipelines generate the target-specific .ll intermediate as part of object emission, regardless of whether --emit-llvm is present. In project mode that flag does not create a second IR file or add an IR-path line to the CLI output. The CLI reports the object and native artifact paths, plus a Windows import-library path when one is produced.
Direct source compilation is different: xenon Main.xe --emit-llvm writes Main.ll beside the first input source and reports that path. For a Windows debug project build of the example, the executable is build/debug/<target-triple>/ExampleApp.exe. Native toolchain requirements and library filenames are described in building libraries.
Common errors
String arrays may span multiple lines and have trailing commas. Each setting must have a unique name and use the syntax shown above. Duplicate settings, unquoted string values and invalid string escapes are rejected.
| Problem | What to check |
|---|---|
Unknown namespace in a using | Is its declaring source file inside source.root? Did you build the project rather than one file? Does the namespace spelling match? |
| A function from another namespace is private | Mark the intended API public. Including the file or adding a using does not change visibility. |
| Multiple project files in a directory | Pass the desired .xeproj explicitly. |
| Source root missing or no sources found | Check the path relative to the project file, the default src directory and the .xe extensions. |
| Multiple functions named Main | Check whether recursive discovery included another executable example or test program. Use separate source roots for separate programs. |
| Unknown setting or profile | Use the supported schema and the built-in debug/release profile names. |
Conditional defines
[build]
defines = ["USE_VULKAN", "ENABLE_DIAGNOSTICS"]
These boolean symbols apply only to this project's source. For a XELIB producer, these local choices are fixed in the library. Invocation flags such as -DENABLE_VALIDATION apply to source projects throughout the build graph. Prebuilt XELIB choices remain fixed. The compiler reserves XENON_ for target/profile built-ins. See conditional compilation for syntax and XELIB target compatibility rules.