Browse Source

bring back plugins

Aleksandr Kuzmenko 6 years ago
parent
commit
6174eb9a96

+ 18 - 0
Makefile

@@ -29,6 +29,19 @@ EXTENSION=
 LFLAGS=
 STATICLINK?=0
 
+SYSTEM_NAME=Unknown
+ifeq ($(OS),Windows_NT)
+	SYSTEM_NAME=Windows
+else
+	UNAME_S := $(shell uname -s)
+	ifeq ($(UNAME_S),Linux)
+		SYSTEM_NAME=Linux
+	endif
+	ifeq ($(UNAME_S),Darwin)
+		SYSTEM_NAME=Mac
+	endif
+endif
+
 # Configuration
 
 ADD_REVISION?=0
@@ -61,6 +74,11 @@ haxe:
 	$(DUNE_COMMAND) build --workspace dune-workspace.dev src/haxe.exe
 	cp -f _build/default/src/haxe.exe ./${HAXE_OUTPUT}
 
+plugin: haxe
+	$(DUNE_COMMAND) build --workspace dune-workspace.dev plugins/$(PLUGIN)/$(PLUGIN).cmxs
+	mkdir -p plugins/$(PLUGIN)/cmxs/$(SYSTEM_NAME)
+	cp -f _build/default/plugins/$(PLUGIN)/$(PLUGIN).cmxs plugins/$(PLUGIN)/cmxs/$(SYSTEM_NAME)/plugin.cmxs
+
 kill_exe_win:
 ifdef SYSTEMROOT
 	-@taskkill /F /IM haxe.exe 2>/dev/null

+ 1 - 0
libs/ilib/dune

@@ -3,6 +3,7 @@
 (library
 	(name ilib)
 	(modules_without_implementation ilData ilMeta)
+	(modules (:standard \ dump))
 	(libraries extlib)
 	(wrapped false)
 )

+ 2 - 1
libs/ttflib/dune

@@ -2,6 +2,7 @@
 
 (library
 	(name ttflib)
-	(libraries extlib extlib_leftovers swflib)
+	(libraries extlib extlib_leftovers swflib unix)
+	(modules (:standard \ main))
 	(wrapped false)
 )

+ 2 - 0
plugins/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 1 - 0
plugins/example/.gitignore

@@ -0,0 +1 @@
+.merlin

+ 24 - 0
plugins/example/README.md

@@ -0,0 +1,24 @@
+# How to build a plugin
+
+```
+$ make plugin PLUGIN=example
+```
+This command builds plugin for current OS only.
+
+# How to use your plugins in a Haxe project
+
+Setup your plugin as a haxe library:
+```
+$ haxelib dev example path/to/haxe/plugins/example
+```
+And then access it inside of a macro:
+```haxe
+macro static public function testPlugin() {
+	Example.plugin.hello();
+	return macro {}
+}
+```
+
+# How to start a new plugin
+
+Just make a copy of an "example" plugin directory and replace all occurrences of "example" word with your own plugin name.

+ 7 - 0
plugins/example/dune

@@ -0,0 +1,7 @@
+(data_only_dirs cmxs hx)
+(include_subdirs unqualified)
+
+(library
+	(name example)
+	(libraries haxe)
+)

+ 12 - 0
plugins/example/haxelib.json

@@ -0,0 +1,12 @@
+{
+	"name" : "example",
+	"url" : "http://haxe.org",
+	"license" : "MIT",
+	"description" : "Example Plugin",
+	"version" : "0.1.0",
+	"releasenote" : "Initial release",
+	"classPath": "hx",
+	"contributors" : ["example"],
+	"tags": ["plugin"],
+	"dependencies" : {}
+}

+ 32 - 0
plugins/example/hx/Example.macro.hx

@@ -0,0 +1,32 @@
+import haxe.PosInfos;
+
+using haxe.io.Path;
+
+typedef ExamplePluginApi = {
+	function hello():Void;
+	function stringifyPosition(p:haxe.macro.Expr.Position):String;
+	function hijackStaticTest():Void;
+}
+
+class Example {
+	/** Access plugin API */
+	static public var plugin(get,never):ExamplePluginApi;
+
+	static var _plugin:ExamplePluginApi;
+	static function get_plugin():ExamplePluginApi {
+		if(_plugin == null) {
+			try {
+				_plugin = eval.vm.Context.loadPlugin(getPluginPath());
+			} catch(e:Dynamic) {
+				throw 'Failed to load plugin: $e';
+			}
+		}
+		return _plugin;
+	}
+
+	static function getPluginPath():String {
+		var currentFile = (function(?p:PosInfos) return p.fileName)();
+		var srcDir = currentFile.directory().directory();
+		return Path.join([srcDir, 'cmxs', Sys.systemName(), 'plugin.cmxs']);
+	}
+}

+ 70 - 0
plugins/example/ml/example.ml

@@ -0,0 +1,70 @@
+open EvalValue
+open Type
+
+class plugin =
+	object (self)
+		(**
+			Prints greeting to stdout.
+			Takes no arguments, returns Void.
+		*)
+		method hello () : value =
+			print_endline "Hello from plugin";
+			(*
+				Plugin architecture requires to return something even for methods typed Void on Haxe side.
+				Return `null`
+			*)
+			vnull
+		(**
+			Takes `haxe.macro.Position` and returns a string of that position in the same format used for
+			compiler errors
+		*)
+		method stringify_position (pos:value) : value =
+			let pos = EvalDecode.decode_pos pos in
+			let str = Lexer.get_error_pos (Printf.sprintf "%s:%d:") pos in
+			EvalEncode.encode_string str
+		(**
+			Change all static methods named "test" to throw "Hello from plugin".
+			This is an example how to modify typed syntax tree.
+		*)
+		method hijack_static_test () : value =
+			let compiler = (EvalContext.get_ctx()).curapi in
+			(**
+				Add a callback like `haxe.macro.Context.onAfterTyping`
+			*)
+			compiler.after_typing (fun haxe_types ->
+				List.iter
+					(fun hx_type ->
+						match hx_type with
+							| TClassDecl cls ->
+								List.iter
+									(fun field ->
+										match field.cf_name, field.cf_expr with
+											| "test", Some e ->
+												let hello = {
+													eexpr = TConst (TString "Hello from plugin");
+													etype = (compiler.get_com()).basic.tstring;
+													epos = Globals.null_pos;
+												} in
+												field.cf_expr <- Some { e with eexpr = TThrow hello }
+											| _ -> ()
+									)
+									cls.cl_ordered_statics
+							| _ -> ()
+					)
+					haxe_types
+			);
+			vnull
+	end
+;;
+
+let api = new plugin in
+
+(**
+	Register our plugin API.
+	This code is executed upon `eval.vm.Context.loadPlugin` call.
+*)
+EvalStdLib.StdContext.register [
+	("hello", EvalEncode.vfun0 api#hello);
+	("stringifyPosition", EvalEncode.vfun1 api#stringify_position);
+	("hijackStaticTest", EvalEncode.vfun0 api#hijack_static_test);
+]

+ 11 - 3
src/dune

@@ -6,18 +6,26 @@
 	)
 )
 
-(executable
+(library
 	(name haxe)
-	(public_name haxe)
-	(package haxe)
 	(libraries
 		extc extproc extlib_leftovers ilib javalib neko objsize pcre swflib ttflib ziplib
 		json
 		unix str threads dynlink
 		xml-light extlib ptmap sha
 	)
+	(modules (:standard \ haxe))
 	(preprocess (per_module
 		((pps sedlex.ppx) json lexer)
 	))
+	(wrapped false)
+)
+
+(executable
+	(name haxe)
+	(public_name haxe)
+	(package haxe)
+	(libraries haxe)
+	(modules haxe)
 	(link_flags (:include ../lib.sexp))
 )