fixed bootstrapping and asset handling

This commit is contained in:
2025-04-11 23:47:07 +02:00
parent f4bbbb9454
commit d73b8bb02e
15 changed files with 1432 additions and 318 deletions

View File

@@ -1,9 +1,238 @@
package macros;
class AssetMacro{
public static function buildAssets(){
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
import sys.FileSystem;
import sys.io.File;
import haxe.Json;
using StringTools;
/**
* Macro for processing @:Assets meta tags and building asset manifests
*/
@:keep
class AssetMacro {
// Track all assets processed
private static var assetMap:Map<String, Array<{id:String, path:String, size:Int, type:String}>> = new Map();
/**
* Entry point for the macro - called during compilation
*/
public static function build():Array<Field> {
#if macro
Sys.println("AssetMacro.build() called - macro is running");
Context.warning("AssetMacro is running", Context.currentPos());
#end
// Get current build context
var fields = Context.getBuildFields();
var localClass = Context.getLocalClass().get();
#if macro
Sys.println("Processing class: " + localClass.name);
#end
// Get the output directory from build.hxml
var outputDir = getOutputDir();
var manifestDir = outputDir + "/manifest";
var assetsOutputDir = outputDir + "/assets";
// Create directories if they don't exist
createDirectory(manifestDir);
createDirectory(assetsOutputDir);
// Process asset fields
for (field in fields) {
processField(field, assetsOutputDir);
}
// Build the manifest files
buildManifests(manifestDir);
return fields;
}
/**
* Process a field for @:Assets meta
*/
private static function processField(field:Field, assetsOutputDir:String) {
// Look for our custom meta
var assetsMeta = findAssetsMeta(field.meta);
if (assetsMeta == null) return;
var params = assetsMeta.params;
if (params.length < 2) {
Context.error("@:Assets meta requires at least 2 parameters: source path and library name", assetsMeta.pos);
return;
}
// Extract parameters
var sourcePath = extractString(params[0]);
var libraryName = extractString(params[1]);
var targetPath = params.length > 2 ? extractString(params[2]) : null;
// Process directory or single file
if (FileSystem.exists(sourcePath)) {
if (FileSystem.isDirectory(sourcePath)) {
processDirectory(sourcePath, libraryName, assetsOutputDir);
} else {
processSingleFile(sourcePath, libraryName, targetPath, assetsOutputDir);
}
} else {
Context.warning('Asset source not found: ${sourcePath}', field.pos);
}
}
/**
* Process a directory of assets
*/
private static function processDirectory(sourcePath:String, libraryName:String, assetsOutputDir:String) {
if (!sourcePath.endsWith("/")) sourcePath += "/";
var targetDir = assetsOutputDir + "/" + libraryName + "/";
createDirectory(targetDir);
function scanDir(path:String, relPath:String = "") {
if (!FileSystem.exists(path)) return;
for (entry in FileSystem.readDirectory(path)) {
var fullPath = path + entry;
var relAssetPath = relPath + entry;
if (FileSystem.isDirectory(fullPath)) {
var newRelPath = relPath + entry + "/";
var newTargetDir = targetDir + newRelPath;
createDirectory(newTargetDir);
scanDir(fullPath + "/", newRelPath);
} else {
// Copy file and register in asset map
var targetPath = targetDir + relAssetPath;
try {
File.copy(fullPath, targetPath);
// Register asset
if (!assetMap.exists(libraryName)) {
assetMap.set(libraryName, []);
}
assetMap.get(libraryName).push({
id: relAssetPath,
path: relAssetPath,
size: FileSystem.stat(fullPath).size,
type: getAssetType(fullPath)
});
} catch (e) {
Context.warning('Failed to copy asset: ${fullPath} -> ${targetPath}. Error: ${e}', Context.currentPos());
}
}
}
}
scanDir(sourcePath);
}
/**
* Process a single file asset
*/
private static function processSingleFile(sourcePath:String, libraryName:String, targetPath:String, assetsOutputDir:String) {
var targetDir = assetsOutputDir + "/" + libraryName + "/";
createDirectory(targetDir);
var fileName = targetPath != null ? targetPath : sourcePath.split("/").pop();
var destPath = targetDir + fileName;
try {
File.copy(sourcePath, destPath);
// Register asset
if (!assetMap.exists(libraryName)) {
assetMap.set(libraryName, []);
}
assetMap.get(libraryName).push({
id: fileName,
path: fileName,
size: FileSystem.stat(sourcePath).size,
type: getAssetType(sourcePath)
});
} catch (e) {
Context.warning('Failed to copy asset: ${sourcePath} -> ${destPath}. Error: ${e}', Context.currentPos());
}
}
/**
* Build manifest JSON files for each library
*/
private static function buildManifests(manifestDir:String) {
for (library in assetMap.keys()) {
var assets = assetMap.get(library);
var manifest = {
name: library,
assets: assets,
rootPath: "../assets/" + library + "/"
};
var manifestPath = manifestDir + "/" + library + ".json";
try {
var content = Json.stringify(manifest, null, " ");
File.saveContent(manifestPath, content);
Context.info('Built manifest: ${manifestPath} with ${assets.length} assets', Context.currentPos());
} catch (e) {
Context.warning('Failed to write manifest: ${manifestPath}. Error: ${e}', Context.currentPos());
}
}
}
// Helper functions
private static function getOutputDir():String {
// Try to parse the --cpp argument from the build.hxml
var args = Sys.args();
for (i in 0...args.length) {
if (args[i] == "--cpp" && i < args.length - 1) {
return args[i + 1];
}
}
return "bin/cpp"; // Default output directory
}
private static function createDirectory(path:String) {
if (!FileSystem.exists(path)) {
try {
FileSystem.createDirectory(path);
} catch (e) {
Context.warning('Failed to create directory: ${path}. Error: ${e}', Context.currentPos());
}
}
}
private static function findAssetsMeta(metaAccess:Array<MetadataEntry>):MetadataEntry {
if (metaAccess == null) return null;
for (meta in metaAccess) {
if (meta.name == ":Assets" || meta.name == "Assets") {
return meta;
}
}
return null;
}
private static function extractString(expr:Expr):String {
switch (expr.expr) {
case EConst(CString(s)): return s;
default: Context.error("Expected string literal", expr.pos);
}
return null;
}
private static function getAssetType(path:String):String {
var ext = path.split(".").pop().toLowerCase();
return (switch (ext) {
case "jpg", "jpeg", "png", "gif", "bmp": "image";
case "mp3", "ogg", "wav": "sound";
case "ttf", "otf": "font";
case "json", "xml", "txt": "text";
default: "binary";
}).toUpperCase();
}
}