I am wondering what is the best way to pass in data to the index.html template using a macro.
I can get a macro to run via the project.xml doing: <haxeflag name="--macro" value="mcro.MyMacros.pageInfoMacro()" />
The problem there is I don’t know what the macro should be doing. Can it affect the Template.globals object for example?
It also seems that Haxe templates have some ability to run macros directly but I’m not sure how to pass a callback function within the OpenFL environment.
Any help is much appreciated, as I am stumped right now!
I found this Haxe cookbook page to be very useful: Working with compiler flags.
Based on that example code and a tip by @player_03 I ended up creating a separate Macro class to avoid error messages about target conflict.
package;
class Macros {
// Shorthand for retrieving compiler flag values.
public static macro function getDefine(key : String) : haxe.macro.Expr {
return macro $v{haxe.macro.Context.definedValue(key)};
}
// Shorthand for setting compiler flags.
public static macro function setDefine(key : String, value : String) : haxe.macro.Expr {
haxe.macro.Compiler.define(key, value);
return macro null;
}
// Shorthand for checking if a compiler flag is defined.
public static macro function isDefined(key : String) : haxe.macro.Expr {
return macro $v{haxe.macro.Context.defined(key)};
}
}
I am currently accessing these macros from my application class in a way like this:
if (Macros.isDefined("configpath")){
loader.load (new URLRequest (Macros.getDefine("configpath")));
} else {
loader.load (new URLRequest ("assets/config.xml"));
}
Given we now have a “setDefine” macro, I should be able to have other macros there to generate the array data I want, set a haxe define, and then access it in my templates using ::DEFINE_MYARRAY:: syntax such as mentioned above.
These three tasks (getDefine(), setDefine(), and isDefined()) are built in, and I’d suggest using the standard library solution rather than rolling your own. That way, if anyone else ever has to look at your code, they’ll already know about the functions you’re using.
//Conditional compilation is a more concise way to check for a define. If you'd
//rather use a macro for some reason, check `Compiler.getDefine() != null`.
#if configpath
//Your `getDefine()` function is a perfect copy of `Compiler.getDefine()`. That
//was probably an accident, but nice coincidence.
loader.load (new URLRequest (Compiler.getDefine ("configpath")));
#else
loader.load (new URLRequest ("assets/config.xml"));
#end
Actually, macros aren’t allowed to call macros, so setDefine() wouldn’t work as written. Instead, your other macros should call Compiler.define() directly.
I think you are getting the hang of macros, given how you correctly implemented a copy of getDefine(). Just remember that Haxe includes a lot of simple macro helpers, so if you need a simple feature, check Context and Compiler before doing it yourself.
Thanks much, I really appreciate the tips! I may be getting the hang of macros but I’m also getting the hang of copy/pasting code from the Haxe cookbook. Coming across that page I didn’t realise that those methods had been implemented in the standard library also.
Oh, I actually forgot to look at the cookbook link you posted. Yeah, that’s worth fixing up a bit, so I submitted a pull request.
Looking at the sample, I can actually see why you might want this setDefine() function. You can use it from outside macros (and only from outside) to set a missing define. Though as I noted in the pull request, it didn’t work as written.
//If a class calls macros, those macros are all always run when compiling the
//class. This will always call `setDefine()` regardless of `isDefined()`.
if (!isDefined("foo")) {
setDefine("foo", "bar");
}
//Conditional compilation always happens before macros. This will only call
//`setDefine()` if `foo` is missing.
#if !foo
setDefine("foo", "bar");
#end
But since you’re using OpenFL, there’s another way that doesn’t require any macros: project.xml.