Version blocks
Version blocks allows one to write platform-specific code. For example:
runThing: class {
version (windows) {
// use CreateProcess ...
return
}
version (!windows) {
// use something more unix-y
}
}
Version blocks support complex binary expressions, like &&
, ||
, !
, and
any depth of parenthesis nesting. For example:
version (!(osx || linux)) {
// not on osx, nor on linux
}
Available version blocks and their corresponding C defines are as follow:
Identifier | Description |
windows | Windows OS, both 32 and 64-bit |
linux | Linux |
solaris | Solaris |
unix | Unices (Apple products do not define this) |
beos | BeOS |
haiku | Haiku (BeOS-like) |
apple | All things apple: iOS, OSX |
ios | iOS: iPhone, iPad |
ios_simulator | iOS compiled for the ios simulator |
osx | Mac OSX (consider using apple instead) |
freebsd | FreeBSD |
openbsd | OpenBSD |
netbsd | NetBSD |
dragonfly | DragonFly BSD |
cygwin | Cygwin toolchain |
mingw | MinGW 32 or 64-bit toolchain |
mingw64 | MinGW 64-bit toolchain |
gnuc | GCC (GNU C) |
msvc | Microsoft Visual C++ |
android | Android toolchain |
arm | ARM processor architecture |
i386 | Intel x86 architecture (defined by GNU C) |
x86 | Intel x86 architecture (defined by MinGW) |
x86_64 | AMD64 architecture |
ppc | PPC architecture |
ppc64 | PPC 64-bit architecture |
64 | 64-bit processor architecture and toolchain |
gc | Garbage Collector activated on compilation |
The specs in italics are “complex” specs - they can’t be in a composed version expression, e.g. this is invalid:
version (64 && osx) {
// code here
}
Consider doing this instead:
version (64) {
version (osx) {
// code here
}
}
The reason for this is that osx
, ios
, and ios_simulator
are not simple #ifdef
s
in the generated code, but they require an include and an equality test.
Line continuations
Any line can be broken down on several lines, by using the backslash character, \
,
as a line continuation:
someList \
map(|el| Something new(el))
If it wasn’t for that \
before the end of the line, map
would be interpreted as
a separate function call, and not a method call.
Constants
Some constants are accessible anywhere and will be replaced at compile time with strings. Those are:
__BUILD_DATETIME__
__BUILD_TARGET__
__BUILD_ROCK_VERSION__
__BUILD_ROCK_CODENAME__
__BUILD_HOSTNAME__
Call chaining
While not technically a preprocessor feature, the following code:
a := ArrayList<Int> new(). add(1). add(2). add(3)
Is equivalent to:
a := ArrayList<Int> new()
a add(1). add(2). add(3)
Itself equivalent to:
a := ArrayList<Int> new()
a add(1)
a add(2)
a add(3)
Slurping
The compiler can read a text file at compile time, and insert it as a string literal in the code.
// assuming 'secret-assets' is a directory we don't ship
secrets := slurp("../secret-assets/secrets.txt")
secrets split("\n") each(|secret|
secret println()
)
slurp
only takes a single argument, and it must be a string
literal (so you can’t compute paths, as that would requiring interpreting
ooc code at compile time, effectively requiring macros).
It is not guaranteed to work with non-text files as it is transformed into an (escaped) string literal.
The path passed to slurp
should be relative to the SourcePath
of the
.use file to which the source file using slurp
corresponds. It is
not relative to the source file itself.
Example directory structure for the example above:
.
├── secret-assets
│ └── secrets.txt
├── source
│ └── foobar
│ └── package
│ └── something.ooc
└── foobar.use
In this example, foobar.use contains SourcePath: source
, as is usual
with ooc libraries.
slurp
is used for example in dye, to ship shader files in the executable.
Note that dye uses that as a fallback, only if it fails to read shader files
from disk, which allows customization without recompiling.