Initial support for modular JS output

This commit is contained in:
Joshua Granick
2016-10-21 15:24:37 -07:00
parent 63a17d8884
commit e14d8e7db0
8 changed files with 599 additions and 1 deletions

View File

@@ -68,6 +68,78 @@
<haxedef name="lime-openal" unless="lime-console || static_link || flash || html5" />
<haxedef name="lime-openal" if="emscripten" />
<!-- TODO: Fix inheritance with separate modules -->
<set name="module-name" value="lime" unless="openfl" />
<set name="module-name" value="openfl" if="openfl" />
<module name="${module-name}" if="html5 modular">
<source path="${HAXE_STD_PATH}" package="js.html" />
<source path="${HAXE_STD_PATH}" package="haxe.ds" />
<source path="${HAXE_STD_PATH}" package="haxe.io" />
<class name="haxe.CallStack" />
<class name="haxe.Log" />
<class name="haxe.Timer" />
<!-- <class name="js.Boot" /> -->
<class name="js.Browser" />
<class name="js.Cookie" />
<class name="js.Error" />
<class name="js.Lib" />
<class name="js.Promise" />
<class name="js.RegExp" />
<class name="js.Selection" />
<class name="js.XMLSocket" />
<class name="EReg" />
<class name="HxOverrides" />
<class name="List" />
<class name="Math" />
<class name="Reflect" />
<!-- <class name="Std" /> -->
<class name="StringBuf" />
<class name="StringTools" />
<class name="Type" />
<include type="haxe.ds._StringMap.StringMapIterator" />
<include type="haxe.ds.TreeNode" />
<include type="haxe.IMap" />
<include type="haxe._Int64.___Int64" />
<include type="haxe.StackItem" />
<include type="js.html._CanvasElement.CanvasUtil" />
<!-- <include type="js._Boot.HaxeError" /> -->
<include type="_List.ListIterator" />
</module>
<module name="${module-name}" if="html5 modular">
<source path="" package="lime" exclude="lime._backend.*|lime.project.*|lime.tools.*|lime.net.*|lime.graphics.console.*" />
<source path="" package="lime" include="lime._backend.html5" />
<class name="lime.net.HTTPRequest" />
<include type="lime.app._Future.FutureWork" />
<include type="lime.graphics.utils._ImageDataUtil.ImageDataView" />
<include type="lime.system._ThreadPool.ThreadPoolMessageType" />
<include type="lime.AssetLibrary" />
<exclude type="lime.graphics.console.IndexBuffer" />
<exclude type="lime.graphics.console.PointerUtil" />
<exclude type="lime.graphics.console.Primitive" />
<exclude type="lime.graphics.console.RenderState" />
<exclude type="lime.graphics.console.Shader" />
<exclude type="lime.graphics.console.Texture" />
<exclude type="lime.graphics.console.TextureAddressMode" />
<exclude type="lime.graphics.console.TextureData" />
<exclude type="lime.graphics.console.TextureFilter" />
<exclude type="lime.graphics.console.TextureFormat" />
<exclude type="lime.graphics.console.VertexBuffer" />
<exclude type="lime.graphics.console.VertexDecl" />
<exclude type="lime.graphics.console.VertexOutput" />
</module>
</section>
<haxelib name="hxcpp" if="setup" />

View File

@@ -49,6 +49,7 @@ class HXProject {
public var libraries:Array <Library>;
public var libraryHandlers:Map <String, String>;
public var meta:MetaData;
public var modules:Map<String, ModuleData>;
public var ndlls:Array <NDLL>;
public var platformType:PlatformType;
public var postBuildCallbacks:Array <CLICommand>;
@@ -234,6 +235,7 @@ class HXProject {
javaPaths = new Array <String> ();
libraries = new Array <Library> ();
libraryHandlers = new Map <String, String> ();
modules = new Map<String, ModuleData> ();
ndlls = new Array <NDLL> ();
postBuildCallbacks = new Array <CLICommand> ();
preBuildCallbacks = new Array <CLICommand> ();
@@ -325,6 +327,12 @@ class HXProject {
ObjectHelper.copyFields (meta, project.meta);
for (key in modules.keys ()) {
project.modules.set (key, modules.get (key).clone ());
}
for (ndll in ndlls) {
project.ndlls.push (ndll.clone ());
@@ -370,7 +378,7 @@ class HXProject {
}
private function filter (text:String, include:Array <String> = null, exclude:Array <String> = null):Bool {
private function filter (text:String, include:Array<String> = null, exclude:Array<String> = null):Bool {
if (include == null) {
@@ -744,6 +752,21 @@ class HXProject {
icons = ArrayHelper.concatUnique (icons, project.icons);
javaPaths = ArrayHelper.concatUnique (javaPaths, project.javaPaths, true);
libraries = ArrayHelper.concatUnique (libraries, project.libraries, true);
for (key in project.modules.keys ()) {
if (modules.exists (key)) {
modules.get (key).merge (project.modules.get (key));
} else {
modules.set (key, project.modules.get (key));
}
}
ndlls = ArrayHelper.concatUnique (ndlls, project.ndlls);
postBuildCallbacks = postBuildCallbacks.concat (project.postBuildCallbacks);
preBuildCallbacks = preBuildCallbacks.concat (project.preBuildCallbacks);

View File

@@ -0,0 +1,57 @@
package lime.project;
import lime.tools.helpers.ArrayHelper;
class ModuleData {
public var classNames:Array<String>;
public var excludeTypes:Array<String>;
public var haxeflags:Array<String>;
public var includeTypes:Array<String>;
public var name:String;
public function new (name:String) {
this.name = name;
classNames = [];
excludeTypes = [];
haxeflags = [];
includeTypes = [];
}
public function clone ():ModuleData {
var copy = new ModuleData (name);
copy.classNames = classNames.copy ();
copy.excludeTypes = excludeTypes.copy ();
copy.haxeflags = haxeflags.copy ();
copy.includeTypes = includeTypes.copy ();
return copy;
}
public function merge (other:ModuleData):Bool {
if (other.name == name) {
classNames = ArrayHelper.concatUnique (classNames, other.classNames);
excludeTypes = ArrayHelper.concatUnique (excludeTypes, other.excludeTypes);
haxeflags = ArrayHelper.concatUnique (haxeflags, other.haxeflags);
includeTypes = ArrayHelper.concatUnique (includeTypes, other.includeTypes);
return true;
}
return false;
}
}

View File

@@ -133,6 +133,10 @@ class ProjectXMLParser extends HXProject {
defines.set ("debug", "1");
} else if (targetFlags.exists ("final")) {
defines.set ("final", "1");
} else {
defines.set ("release", "1");
@@ -754,6 +758,191 @@ class ProjectXMLParser extends HXProject {
}
private function parseModuleElement (element:Fast, basePath:String = "", moduleData:ModuleData = null):Void {
var topLevel = (moduleData == null);
var exclude = "";
var include = "*";
if (element.has.include) {
include = substitute (element.att.include);
}
if (element.has.exclude) {
exclude = substitute (element.att.exclude);
}
if (moduleData == null) {
var name = substitute (element.att.name);
if (modules.exists (name)) {
moduleData = modules.get (name);
} else {
moduleData = new ModuleData (name);
modules.set (name, moduleData);
}
}
switch (element.name) {
case "module":
if (element.has.source) {
var source = PathHelper.combine (basePath, substitute (element.att.source));
if (!FileSystem.exists (source)) {
LogHelper.error ("Could not find module source \"" + source + "\"");
return;
}
moduleData.haxeflags.push ("-cp " + source);
var path = source;
if (element.has.resolve ("package")) {
path = PathHelper.combine (source, StringTools.replace (substitute (element.att.resolve ("package")), ".", "/"));
}
parseModuleElementSource (source, moduleData, include.split ("|"), exclude.split ("|"), path);
}
case "source":
if (element.has.path) {
var source = PathHelper.combine (basePath, substitute (element.att.path));
if (!FileSystem.exists (source)) {
LogHelper.error ("Could not find module source \"" + source + "\"");
return;
}
moduleData.haxeflags.push ("-cp " + source);
var path = source;
if (element.has.resolve ("package")) {
path = PathHelper.combine (source, StringTools.replace (substitute (element.att.resolve ("package")), ".", "/"));
}
parseModuleElementSource (source, moduleData, include.split ("|"), exclude.split ("|"), path);
}
case "class":
moduleData.classNames.push (substitute (element.att.name));
case "haxedef":
var value = substitute (element.att.name);
if (element.has.value) {
value += "=" + substitute (element.att.value);
}
moduleData.haxeflags.push ("-D " + value);
case "haxeflag":
var flag = substitute (element.att.name);
if (element.has.value) {
flag += " " + substitute (element.att.value);
}
moduleData.haxeflags.push (substitute (flag));
case "include":
moduleData.includeTypes.push (substitute (element.att.type));
case "exclude":
moduleData.excludeTypes.push (substitute (element.att.type));
}
if (topLevel) {
for (childElement in element.elements) {
if (isValidElement (childElement, "")) {
parseModuleElement (childElement, basePath, moduleData);
}
}
}
}
private function parseModuleElementSource (source:String, moduleData:ModuleData, include:Array<String>, exclude:Array<String>, currentPath:String):Void {
var files = FileSystem.readDirectory (currentPath);
var filePath, className;
for (file in files) {
filePath = PathHelper.combine (currentPath, file);
if (FileSystem.isDirectory (filePath)) {
parseModuleElementSource (source, moduleData, include, exclude, filePath);
} else {
if (Path.extension (file) != "hx") continue;
className = StringTools.replace (filePath, source, "");
className = StringTools.replace (className, "\\", "/");
while (StringTools.startsWith (className, "/")) className = className.substr (1);
className = StringTools.replace (className, "/", ".");
className = StringTools.replace (className, ".hx", "");
if (filter (className, include, exclude)) {
moduleData.classNames.push (className);
}
}
}
}
private function parseOutputElement (element:Fast):Void {
if (element.has.name) {
@@ -1367,6 +1556,10 @@ class ProjectXMLParser extends HXProject {
}
case "module":
parseModuleElement (element, extensionPath);
case "ssl":
//if (wantSslCertificate())

View File

@@ -111,4 +111,53 @@ class ArrayHelper {
}
public static function getUnique<T> (a:Array<T>, b:Array<T>, key:String = null):Array<T> {
if (a == null && b == null) {
return new Array<T> ();
} else if (a == null && b != null) {
return b;
}
var concat = [];
for (bValue in b) {
var hasValue = false;
for (aValue in a) {
if (key != null) {
if (Reflect.field (aValue, key) == Reflect.field (bValue, key)) {
hasValue = true;
}
} else if (aValue == bValue) {
hasValue = true;
}
}
if (!hasValue) {
concat.push (bValue);
}
}
return concat;
}
}

View File

@@ -0,0 +1,179 @@
package lime.tools.helpers; #if !macro
import lime.project.Dependency;
import lime.project.Haxelib;
import lime.project.HXProject;
import sys.io.File;
class ModuleHelper {
public static function buildModules (project:HXProject, tempDirectory:String, outputDirectory:String):Void {
tempDirectory = PathHelper.combine (tempDirectory, "lib");
outputDirectory = PathHelper.combine (outputDirectory, "lib");
PathHelper.mkdir (tempDirectory);
PathHelper.mkdir (outputDirectory);
var importName, hxmlPath, importPath, outputPath, moduleImport, hxml;
for (module in project.modules) {
if (module.classNames.length > 0) {
importName = "Module" + module.name.charAt (0).toUpperCase () + module.name.substr (1);
hxmlPath = PathHelper.combine (tempDirectory, module.name + ".hxml");
importPath = PathHelper.combine (tempDirectory, importName + ".hx");
if (project.targetFlags.exists ("final")) {
outputPath = PathHelper.combine (outputDirectory, module.name + ".min.js");
} else {
outputPath = PathHelper.combine (outputDirectory, module.name + ".js");
}
moduleImport = "package;\n\nimport " + module.classNames.join (";\nimport ") + ";";
hxml = "-cp " + tempDirectory;
hxml += "\n" + module.haxeflags.join ("\n");
hxml += "\n-cp " + PathHelper.getHaxelib (new Haxelib ("lime"));
for (key in project.haxedefs.keys ()) {
if (key != "no-compilation") {
var value = project.haxedefs.get (key);
if (value == null || value == "") {
hxml += "\n-D " + key;
} else {
hxml += "\n-D " + key + "=" + value;
}
}
}
hxml += "\n-D html5";
hxml += "\n-D html";
hxml += "\n--no-inline";
hxml += "\n-dce no";
hxml += "\n-js " + outputPath;
var includeTypes = module.classNames.concat (module.includeTypes);
var excludeTypes = module.excludeTypes;
for (otherModule in project.modules) {
if (otherModule != module) {
excludeTypes = excludeTypes.concat (ArrayHelper.getUnique (includeTypes, otherModule.classNames));
excludeTypes = excludeTypes.concat (ArrayHelper.getUnique (includeTypes, otherModule.includeTypes));
}
}
if (excludeTypes.length > 0) {
hxml += "\n--macro lime.tools.helpers.ModuleHelper.exclude(['" + excludeTypes.join ("','") + "'])";
}
hxml += "\n--macro lime.tools.helpers.ModuleHelper.expose(['" + includeTypes.join ("','") + "'])";
hxml += "\n" + importName;
File.saveContent (importPath, moduleImport);
File.saveContent (hxmlPath, hxml);
ProcessHelper.runCommand ("", "haxe", [ hxmlPath ]);
if (project.targetFlags.exists ("final")) {
HTML5Helper.minify (project, outputPath);
}
}
}
}
public static function updateProject (project:HXProject):Void {
var excludeTypes = [];
var suffix = (project.targetFlags.exists ("final") ? ".min" : "") + ".js";
for (module in project.modules) {
project.dependencies.push (new Dependency ("./lib/" + module.name + suffix, null));
excludeTypes = ArrayHelper.concatUnique (excludeTypes, module.classNames);
excludeTypes = ArrayHelper.concatUnique (excludeTypes, module.excludeTypes);
excludeTypes = ArrayHelper.concatUnique (excludeTypes, module.includeTypes);
}
if (excludeTypes.length > 0) {
project.haxeflags.push ("--macro lime.tools.helpers.ModuleHelper.exclude(['" + excludeTypes.join ("','") + "'])");
}
}
}
#else
import haxe.macro.Compiler;
class ModuleHelper {
public static function exclude (types:Array<String>):Void {
for (type in types) {
Compiler.exclude (type);
Compiler.addMetadata ("@:native(\"$hx_exports." + type + "\")", type);
}
}
public static function expose (classNames:Array<String>):Void {
for (className in classNames) {
Compiler.addMetadata ("@:expose('" + className + "')", className);
}
}
}
#end

View File

@@ -9,6 +9,7 @@ import lime.tools.helpers.FileHelper;
import lime.tools.helpers.HTML5Helper;
import lime.tools.helpers.IconHelper;
import lime.tools.helpers.LogHelper;
import lime.tools.helpers.ModuleHelper;
import lime.tools.helpers.PathHelper;
import lime.tools.helpers.ProcessHelper;
import lime.project.AssetType;
@@ -36,6 +37,8 @@ class HTML5Platform extends PlatformTarget {
public override function build ():Void {
ModuleHelper.buildModules (project, targetDirectory + "/obj", targetDirectory + "/bin");
if (project.app.main != null) {
var type = "release";
@@ -169,6 +172,8 @@ class HTML5Platform extends PlatformTarget {
}
ModuleHelper.updateProject (project);
var context = project.templateContext;
context.WIN_FLASHBACKGROUND = project.window.background != null ? StringTools.hex (project.window.background, 6) : "";

View File

@@ -1395,6 +1395,26 @@ class CommandLineTools {
}
if (Sys.getEnv ("HAXEPATH") == null) {
if (PlatformHelper.hostPlatform == Platform.WINDOWS) {
Sys.putEnv ("HAXEPATH", "C:\\HaxeToolkit\\haxe\\");
} else {
Sys.putEnv ("HAXEPATH", "/usr/lib/haxe");
}
}
if (Sys.getEnv ("HAXE_STD_PATH") == null) {
Sys.putEnv ("HAXE_STD_PATH", PathHelper.combine (Sys.getEnv ("HAXEPATH"), "std"));
}
if (project == null) {
HXProject._command = command;