On this page

25. Preprocessing and includes

25.1 Conditional compilation

CS-PREPROCESS-001. Preprocessing is always part of compilation and occurs before normal parsing. Inactive branches are excluded and need not parse as DirectSharp. Conditional directives use #if, #elif, #else, #endif, #ifdef and #ifndef; Boolean expressions use not, and and or.

#define ENABLE_REPORTS = true

#if __compiler__.define("ENABLE_REPORTS")
public action PrintReportLocation(path as String)
    Global.System.Console.WriteLine("Report written to {{path}}")
end action
#else
public action PrintReportLocation(path as String)
end action
#endif

Directives are recognized at the start of legal logical source lines. Directive-looking text in ordinary strings/comments is not a directive.

25.2 Typed definitions

CS-PREPROCESS-002. #define NAME = value introduces a typed Boolean, String or numeric scalar compiler definition; #undef NAME removes it in the applicable local preprocessing scope. Existence tests do not test truthiness: a definition with value false still satisfies #ifdef.

CS-PREPROCESS-003. Definitions may come from solution, project and local sources. A conflict is not silently shadowed or resolved by last declaration wins. Use #define override NAME = value to intentionally replace an existing visible, replaceable definition in the applicable preprocessing scope. A missing replacement target is a diagnostic; compiler-reserved restrictions still apply. Ordinary conflicting #define does not silently replace a definition. Local replacement/undefinition does not mutate persistent solution/project configuration.

#define MODE = "production"
#define override MODE = 'test'

Both quote forms delimit the same String type, including in directives.

25.3 Explicit substitution

CS-PREPROCESS-004. Bare identifiers are never automatic text macros. Explicit ${__compiler__.define("NAME")} substitutes a definition as text. An additional $ escapes the marker: $${__compiler__.define("NAME")} emits the marker literally. An undefined live placeholder is an error.

Substitution renders Booleans as true/false, numerics using invariant culture and Strings as their text without automatically adding quotes. It applies to active content, including strings/comments, and is distinct from ordinary String interpolation. Inactive content does not require live placeholder resolution.

25.4 Include semantics

CS-PREPROCESS-005. #include "relative/path.inc" and #include 'relative/path.inc' are equally valid; include paths accept either ordinary String quote form. The path is a literal, not a runtime expression. .inc fragments are not independently compiled units. Inclusion processes a fragment at the inclusion point, recursively applying conditionals, definitions, substitution and nested inclusion. Paths are resolved relative to the including source/fragment within configured source/project boundaries. Include cycles are errors with an include-chain diagnostic. Source maps preserve original file/line/column locations.

#include "shared/contracts.inc"
#include 'shared/helpers.inc'

These examples assume those fragments exist relative to the including file within the configured boundaries. Normal String delimiter escaping applies; backslash is literal, not a general escape character.

Preprocessing supplies no function-like/variadic macros, token pasting, automatic bare-identifier replacement, unrestricted environment expansion or runtime processing. It must not mutate package/reference configuration. Target-native source injection, where supported, is a distinct visibly target-specific mechanism.


Specification contents · Previous: 24. Wrappers · Next: 26. Project model