Compile-Time Context

Four values describe the build a program is part of. The compiler fills them in before compiling starts, and Core declares them, so each is imported by its # name:

import Core::{ #target, #build, #compiler, #source };
ValueTypeDescribes
#targetTargetthe machine the program is built for
#buildBuildthe build profile, its options and the output
#compilerCompilerthe compiler doing the build
#sourceSourcethe place in the source where the read is written

A fifth, #config, carries values the build is given — see Config. The struct and enum types (Target, OperatingSystem, BuildMode, …) are ordinary Core declarations; import them too when code names them, as in OperatingSystem::Linux or BuildMode::Debug.

Fields are read with ., are lowerCamelCase, and are fixed for the whole build. A read can drive a when, initialize a scalar constant, or appear in any expression, where it compiles to a constant. Text fields (char8[..]) work in expressions and conditions; a const of slice type, though, must be initialized with a literal, so const Date: char8[..] = #build.date; is refused with a constant sequence must be initialized with an array literal or a string literal — use a let instead.

#target

The machine the program will run on — not the machine compiling it. rux build --target linux-x86_64 on Windows answers every field for Linux.

Field / queryTypeNotes
osOperatingSystemthe operating system
archArchitecturethe processor architecture
abiApplicationBinaryInterfacethe calling and layout conventions
endianEndiannessbyte order; .Little on every supported target
pointerBitsuint64 on every supported target
dataModelDataModelthe C widths of long and pointers
objectFormatObjectFormatthe object format the linker emits
triplechar8[..]the canonical <os>-<arch> name --target takes
HasFeature(feature)boolwhether the build enables a CPU feature

The eight supported targets:

tripleosarchabidataModelobjectFormat
windows-x86_64.Windows.X86_64.WindowsX64.LLP64.COFF
windows-aarch64.Windows.AArch64.AAPCS64.LLP64.COFF
linux-x86_64.Linux.X86_64.SystemV.LP64.ELF
linux-aarch64.Linux.AArch64.AAPCS64.LP64.ELF
macos-x86_64.macOS.X86_64.SystemV.LP64.MachO
macos-aarch64.macOS.AArch64.AAPCS64.LP64.MachO
freebsd-x86_64.FreeBSD.X86_64.SystemV.LP64.ELF
freebsd-aarch64.FreeBSD.AArch64.AAPCS64.LP64.ELF

HasFeature takes a TargetFeature. A build for the machine running the compiler reports the features that compiler was itself built to use; a build for any other target reports none. Inside a when condition the shorthand .AVX2 works; in an ordinary expression write TargetFeature::AVX2. An unknown name is an error (unknown target feature '.MMX').

import Core::{ #target };

when #target.arch == .X86_64 && #target.HasFeature(.AVX2) {
    func Kernel() -> char8[..] { return "avx2"; }
} else {
    func Kernel() -> char8[..] { return "scalar"; }
}

#build

The build profile and what it produces. Every field is the same everywhere in one build.

FieldTypeDebug profileRelease profile
profilechar8[..]"Debug""Release"
modeBuildMode.Debug.Release
optimizationOptimizationMode.None.Speed
debugAssertionsbooltruefalse
debugInfobooltruefalse
FieldTypeValue
isTestbooltrue in a rux test build
outputKindOutputKindthe artifact: .Executable, .SharedLibrary, .StaticLibrary or .SourceLibrary
timestampuint64when the build started, in seconds since the Unix epoch
datechar8[..]timestamp as YYYY-MM-DD, in UTC
timechar8[..]timestamp as HH:MM:SS, in UTC

profile is a name, and a project may define profiles of its own; branch on mode, which every profile has. debugAssertions decides whether DebugAssert is kept — when it is false the call is removed and its arguments are not evaluated. The timestamp is taken once per build, and honours the SOURCE_DATE_EPOCH environment variable for reproducible builds.

import Core::{ #build, BuildMode };
import Io::PrintLine;

func Main() -> int {
    when #build.mode == BuildMode::Debug {
        PrintLine("debug build of {} at {}", #build.date, #build.time);
    }
    return 0;
}

#compiler

Field / queryTypeNotes
versionSemanticVersionthe compiler's version, with major, minor, patch (uint)
HasFeature(name)boolwhether the compiler reports a named feature

SemanticVersion is an ordinary struct with the comparison operators == != < <= > >= and the methods Compare, IsEqualTo, IsLessThan, IsAtMost, IsGreaterThan and IsAtLeast, all of which work in a when. A version to compare against is a struct literal constant — a constructor call is a run-time call and is refused (call to 'SemanticVersion' is not a compile-time value):

import Core::{ #compiler, SemanticVersion };

const Rux040 = SemanticVersion { major: 0, minor: 4, patch: 0 };

when #compiler.version.IsAtLeast(Rux040) {
    const HasDefines: bool = true;
} else {
    const HasDefines: bool = false;
}

HasFeature is the better test when code needs one capability rather than a release: it names what is needed and stays right however releases are numbered. Its argument must be a string literal; an unknown name is simply false. Rux 0.4.0 reports:

Feature nameFeature nameFeature name
"conditional-compilation""namespaced-intrinsics""target-intrinsics"
"build-intrinsics""compiler-feature-detection""source-location-defaults"
"extern-symbol-names""link-attribute""no-return-attribute"

#source

Each read of #source describes the place where that read is written — the expression itself, not the function around it or its caller.

FieldTypeValue
lineuintthe one-based line of the read
columnuintthe one-based column of the read
filechar8[..]the file name as the build names it, such as "Main.rux"
fileNamechar8[..]the file's last path component, such as "Main.rux"
filePathchar8[..]the path inside the package, such as "Src/Main.rux"
functionchar8[..]the enclosing function, qualified for a method ("Point::Length") or a module function ("Tools::Parse")
modulechar8[..]the enclosing module path
#source.module includes the file name.
rux 0.4.0 reports the module path prefixed with the source file's name — "Main::Tools" for a read inside module Tools in Main.rux, and "Main" at the package root. Files are not modules, so do not build logic on that prefix.

A helper that reads #source in its own body always reports its own body. To report where it was called from, the caller reads #source and passes the value:

import Core::{ #source };
import Io::PrintLine;

func LogAt(line: uint, message: char8[..]) {
    PrintLine("line {}: {}", line, message);
}

func Main() -> int {
    LogAt(#source.line, "first call");    // the line of this call
    LogAt(#source.line, "second call");   // and of this one
    return 0;
}

The same rule covers a default argument. func LogAt(message: char8[..], line: uint = #source.line) reports the line where the default is written — the parameter list — for every call, so a default does not capture the caller's location. Pass the location explicitly, as above.

Enum reference

Every enum here is a Core declaration with a uint8 representation.

EnumVariants
OperatingSystemUnknown, FreeBSD, Linux, macOS, Windows
ArchitectureUnknown, AArch64, X86_64
ApplicationBinaryInterfaceUnknown, AAPCS64, SystemV, WindowsX64
EndiannessBig, Little
DataModelUnknown, LLP64, LP64
ObjectFormatUnknown, COFF, ELF, MachO
TargetFeatureAVX, AVX2, AVX512, NEON, SSE2, SSE3, SSE41, SSE42, SSSE3, SVE
BuildModeDebug, Release
OptimizationModeNone, Size, Speed
OutputKindExecutable, SharedLibrary, StaticLibrary, SourceLibrary

No supported build produces an Unknown variant; it exists so a provider can describe a target the compiler has no name for. The variant is .macOS, with a lower-case m.

See also