Haxe Conditional Compilation: Keep Platform Code Out of Your Build
Use Haxe #if directives and -D flags to exclude platform-specific code from builds where it does not apply, with verification steps for JavaScript and C++ targets.
23 Jun 2026, 09:46 UTC

Two Targets, One Codebase, and APIs That Don't Mix
You are building a Haxe application for JavaScript and C++. The JavaScript build needs to read the browser DOM, while the C++ build needs to read local files. If you write a runtime check such as if (isJs), both code paths travel with every build. The C++ binary carries JavaScript-only calls it can never use, and the web bundle carries native calls that will fail if executed.
The useful takeaway: Haxe can remove the wrong branch before it generates target code. Conditional compilation keeps each build specific to its platform without runtime cost.
How Haxe Evaluates #if
Haxe provides #if, #elseif, #else, and #end. These are compiler directives, not runtime statements. The compiler evaluates them while translating Haxe source into target-specific code. A branch that does not match the current target or custom define is dropped from the generated output.
Target names such as js and cpp are built-in, but exact flag names and behavior can differ between Haxe versions. Check your compiler's documentation or run haxe --help before relying on a specific target flag.
Worked Example: Platform Notification
This example shows one function that behaves differently per target. On JavaScript it uses js.Browser.alert; on C++ it uses Sys.println. Other targets fall back to trace.
class Platform {
public static function notify(message:String):Void {
#if js
js.Browser.alert(message);
#elseif cpp
Sys.println(message);
#else
trace(message);
#end
}
}
Save this as src/Platform.hx. A caller can invoke Platform.notify("Ready"); without knowing which target is running.
Compiling for Each Target
Run these commands from your project directory. You need write permission for the output directory, and the target toolchain must be installed, for example a C++ compiler for the C++ target.
# JavaScript output
haxe -js build/js/main.js -main Main
# C++ output
haxe -cpp build/cpp -main Main
Replace Main with your entry class. The compiler prints errors to the terminal. If compilation succeeds, inspect the generated files rather than the source to confirm which branch survived.
Custom Build Flags
You can define your own flags with -D. This is useful for feature toggles such as verbose logging.
#if verbose
trace("Verbose: processing request");
#end
Compile with the flag:
haxe -D verbose -js build/js/main.js -main Main
Omit -D verbose and the trace call is removed from the JavaScript output. You can also place -D verbose in an .hxml build file; command-line and hxml defines are both processed by the compiler, though exact precedence depends on your Haxe version.
Limitations and How to Check
- Hidden errors in inactive branches. Code inside a branch the compiler skips may not surface until you build that specific target. A syntax error in the C++ branch can pass unnoticed during a JavaScript build.
- Generated output layout varies. The names and locations of generated files depend on the Haxe version and target. Do not assume a specific file path.
- Maintenance. Scattering
#ifthroughout business logic makes the code harder to read. Keep platform-specific calls in one module or abstract type.
Practical verification:
- Compile for JavaScript.
- Open the generated JavaScript file and search for a string unique to the C++ branch, such as
Sys.println. It should be absent. - Compile for C++.
- Inspect the generated C++ files and confirm the JavaScript branch, such as
js.Browser.alert, is not present. - Repeat for every target you support, ideally in CI, so inactive branches are exercised by at least one build.
Closing
Conditional compilation is a compile-time filter, not a runtime switch. Use it to keep target-specific APIs out of builds where they do not belong, but verify each target separately because errors in skipped branches can stay hidden until that target is compiled.
If you change build flags and want to return to a previous configuration, delete the generated output directory and rebuild with the original flags; the source files are unchanged.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.