I'd like to dynamically add some new Types to a given Module based on some files found in a directory.
I'm essentially trying to populate a bunch of @:file(...)
embed classes at the bottom of a Module.
//This is the module I'm targeting to append embedded ByteArray subtypes (see below)
@:build(macros.AutoEmbed.build("some/folder/"))
class Embeds {
//Empty on purpose, just let the Macro do its thing!
}
// At "macro-time", it should generate these:
@:file("some/folder/ui_main.xml")
class UI_MAIN_XML extends flash.utils.ByteArray { }
@:file("some/folder/config.template.json")
class CONFIG_TEMPLATE_JSON extends flash.utils.ByteArray { }
What I've been able to find so far is that I might have to alter the Embeds.hx
module. So I looked into Context.getModule( Context.getLocalModule() )
. I've also looked into TypeDefinition
since it seems like the only way to define a new type from scratch.
The problem with Context.getModule(...)
though is that it returns an array Array<Type>
, not Array<TypeDefinition>
, so I can't append new TypeDefinition
to it (plus I have to figure out how to write those, ughh). That's probably a bad assumption on my part, but I thought by simply appending more TypeDefinition
to it I could dynamically provide more types in the module.
I'm still very new to Macros as you can tell!
EDIT
It's true that I could just dynamically write/overwrite a new Embeds.hx
file at compile-time with a FileSystem / File write solution, but that implies needing to compile at least once before your IDE's auto-completion can pickup the generated Embeds.*
classes (FlashDevelop in my case). Plus anytime new files are dropped in the defined folder, same problem: you need to compile first before the IDE detects it. Yes, I really like auto-completion :)
hx of the Haxe Standard Library. Figure: The role of macros during compilation. A basic macro is a syntax-transformation. It receives zero or more expressions and also returns an expression. If a macro is called, it effectively inserts code at the place it was called from.
Initialization macros are invoked from the command line by using the --macro callExpr(args) command. This registers a callback which the compiler invokes after creating its context, but before typing the argument to --main . This then allows configuring the compiler in some ways.
Starting from the build macro is good. You can build class fields and create types.
Here's a macro which will just generate one type and a corresponding field:
#if macro
import haxe.macro.Context;
import haxe.macro.Expr;
class AutoEmbed
{
macro static public function build(folder:String):Array<Field>
{
var inClass = Context.getLocalClass().get();
// explore folder and create those:
var fileName = 'resource/strings.json';
var name = fileName.split('/').pop().split('.').join('_');
var valueExpr = makeType(inClass.pack, name, fileName);
var field = {
name: name,
access: [APublic, AStatic, AInline],
kind: FVar(null, valueExpr),
pos: Context.currentPos()
}
return [field];
}
static function makeType(pack:Array<String>, name:String, fileName:String)
{
var pos = Context.currentPos();
var className = name.toUpperCase();
var cdef = macro class Tmp extends haxe.io.BytesData { }
cdef.pack = pack.copy();
cdef.name = className;
cdef.meta = [{
name: ':file',
params: [Context.makeExpr(fileName, pos)],
pos: pos
}];
haxe.macro.Context.defineType(cdef);
return {
expr:EConst(CIdent(className)),
pos:pos
};
}
}
#end
Now to use it:
trace(Embed.strings_json); // [ByteArray]
@:build(AutoEmbed.build('some/folder'))
class Embeds
{
// generate field strings_json pointing to class STRINGS_JSON
}
You can use initialization macro : http://haxe.org/manual/macro-initialization.html to execute macro before typing occurs.
Then to actually create new classes/modules you can use Context.defineModule : http://api.haxe.org/haxe/macro/Context.html#defineModule
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With