SCUA

Manual

Conditional compilation

Sometimes you want the same source to build differently for different targets: a debug build with extra diagnostics, a pro edition with features the free one lacks. comptime if chooses between branches at compile time, based on flags you pass on the command line. The branch that wins is compiled; the others aren't in the program at all.

#comptime if and build flags

comptime if <cond> then ... end reads from build, a config object the host fills in from the command line. You set a flag with -D name=value. A bare -D name (no value) means true. A flag you don't pass is absent, which is falsy.

comptime if build.debug then
  info(`debug diagnostics enabled`)
end

print("game starting")

With no flags, build.debug is absent, so the block is stripped:

$ scua conditional.scua
game starting

Pass -D debug and the block is compiled in:

$ scua -D debug conditional.scua
[info] debug diagnostics enabled
game starting

This is a compile-time choice, not a runtime if. The losing branch isn't skipped while the program runs; it's never compiled, so it adds nothing to the build.

#Comparing typed flags

Flags can carry values, and you can compare them. The elseif/else chain works like an ordinary if, except it's resolved at compile time:

comptime if build.tier == "pro" then
  print("pro edition: all features unlocked")
elseif build.tier == "plus" then
  print("plus edition")
else
  print("free edition")
end

An unset build.tier is absent, so both comparisons are false and the else wins:

$ scua conditional.scua
free edition

Set it and a different branch is selected:

$ scua -D tier=pro conditional.scua
pro edition: all features unlocked
$ scua -D tier=plus conditional.scua
plus edition

You can set several flags at once. Here's the boolean gate and the tier gate together in one build:

$ scua -D debug -D tier=pro conditional.scua
[info] debug diagnostics enabled
pro edition: all features unlocked
game starting

#Unselected branches are parsed but not checked

Every branch has to parse, so the syntax always has to be valid and tooling can see the whole file. But only the selected branch is type-checked and compiled. An unselected branch can refer to things that don't exist in this build:

comptime if build.pro then
  grant_pro_capabilities()   -- only compiled in a pro build
end

print("game starting")

With no -D pro, that branch is dropped before checking, so the missing grant_pro_capabilities is fine:

$ scua conditional.scua
game starting

This is what makes comptime if more than a runtime if: the dead branch can mention functions, fields, or values that are only present in another build, and you won't get an error for them here. Build for the pro target and that branch is the one that gets checked, where the name does need to exist.

A couple of things to keep in mind. build is readable only inside a comptime if condition, not in ordinary code. And because the choice is made at compile time, a given run of scua produces one fixed program; to get the other branch, build again with different flags.

  • Logging for the other compile-time switch, dropping log statements below a level floor.
  • Command-line interface for the full set of flags, including -D.