9.6.3 Build Order

The build order of types is unspecified and this extends to the execution order of build-macros. While certain rules can be determined, we strongly recommend to not rely on the execution order of build-macros. If type building requires multiple passes, this should be reflected directly in the macro code. In order to avoid multiple build-macro executions on the same type, the state can be stored in persistent variables or added as metadata to the type in question:

import haxe.macro.Context;
import haxe.macro.Expr;

#if !macro
@:autoBuild(MyMacro.build())
#end
interface I1 {}
#if !macro
@:autoBuild(MyMacro.build())
#end
interface I2 {}
class C implements I1 implements I2 {}

class MyMacro {
  macro static public function build():Array<Field> {
    var c = Context.getLocalClass().get();
    if (c.meta.has(":processed"))
      return null;
    c.meta.add(":processed", [], c.pos);
    // process here
    return null;
  }
}

class Main {
  static public function main() {}
}

With both interfaces I1 and I2 having :autoBuild metadata, the build macro is executed twice for class C. We guard against duplicate processing by adding a custom :processed metadata to the class, which can be checked during the second macro execution.