├── hlextLib ├── makefile.mac ├── makefile.linux ├── makefile.win ├── hlext.c └── hlext │ └── HLExt.hx ├── docker-compose.yml ├── Main.hx ├── Dockerfile.linux ├── Dockerfile.win └── README.md /hlextLib/makefile.mac: -------------------------------------------------------------------------------- 1 | hlext.hdll: hlext.c 2 | gcc -o hlext.hdll hlext.c -shared -Wall -O3 -I. -std=c11 -I/usr/local/include -lhl -------------------------------------------------------------------------------- /hlextLib/makefile.linux: -------------------------------------------------------------------------------- 1 | hlext.hdll: hlext.c 2 | gcc -o hlext.hdll hlext.c -shared -Wall -O3 -I. -std=c11 -fPIC -I/usr/local/include -lhl -------------------------------------------------------------------------------- /hlextLib/makefile.win: -------------------------------------------------------------------------------- 1 | all: hlext.dll 2 | 3 | hlext.dll: hlext.obj 4 | cl /LD /MD hlext.obj "$(HASHLINK_BIN)\libhl.lib" 5 | 6 | hlext.obj: hlext.c 7 | cl /c /MD hlext.c /I . /I $(HASHLINK) 8 | 9 | .PHONY: all -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | linux: 4 | build: 5 | context: . 6 | dockerfile: Dockerfile.linux 7 | volumes: 8 | - .:/usr/local/share/host 9 | win: 10 | build: 11 | context: . 12 | dockerfile: Dockerfile.win 13 | volumes: 14 | - .:c:\host -------------------------------------------------------------------------------- /Main.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import hlext.HLExt; 4 | 5 | class Main { 6 | 7 | static public function main() { 8 | 9 | var d = Date.now(); 10 | var inst = new HLExt('HashLink', 2015, d.getFullYear()); 11 | 12 | trace('Result managed in C side / greeting: ' + inst.getGreeting()); 13 | trace('Result managed in C side / age: ' + inst.getAge()); 14 | 15 | var result:HLExtResult = HLExt.getHaxeObject('Josu', 1985, d.getFullYear()); 16 | trace('Result managed in Haxe side / greeting: ' + result.greeting); 17 | trace('Result managed in Haxe side / age: ' + result.age); 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /Dockerfile.linux: -------------------------------------------------------------------------------- 1 | FROM haxe:4.0.5-stretch 2 | 3 | # HASHLINK 4 | ENV HL_VERSION 1.10 5 | RUN set -ex \ 6 | && hlBuildDeps=' \ 7 | gcc \ 8 | g++ \ 9 | make \ 10 | cmake \ 11 | libpng-dev \ 12 | libturbojpeg-dev \ 13 | libvorbis-dev \ 14 | libopenal-dev \ 15 | libsdl2-dev \ 16 | libmbedtls-dev \ 17 | libuv1-dev \ 18 | ' \ 19 | && apt-get update && apt-get install -y $hlBuildDeps --no-install-recommends && rm -rf /var/lib/apt/lists/* \ 20 | \ 21 | && wget -O hashlink.tar.gz "https://github.com/HaxeFoundation/hashlink/archive/1.10.tar.gz" \ 22 | && mkdir -p /usr/src/hl \ 23 | && tar -xC /usr/src/hl --strip-components=1 -f hashlink.tar.gz \ 24 | && rm hashlink.tar.gz \ 25 | && cd /usr/src/hl \ 26 | && make \ 27 | && make install \ 28 | && cp /usr/local/lib/libhl.so /usr/lib/ \ 29 | && rm -r /usr/src/hl 30 | # && apt-get purge -y --auto-remove $hlBuildDeps \ 31 | 32 | ENTRYPOINT ["tail", "-f", "/dev/null"] -------------------------------------------------------------------------------- /hlextLib/hlext.c: -------------------------------------------------------------------------------- 1 | #define HL_NAME(n) hlext_##n 2 | 3 | #include 4 | 5 | typedef struct _result hlext_result; 6 | 7 | struct _result { 8 | vbyte *greeting; 9 | int age; 10 | }; 11 | 12 | HL_PRIM hlext_result *HL_NAME(request_result)( vbyte* name, int birthYear, int currentYear ) { 13 | 14 | hlext_result *cr = (hlext_result*)hl_gc_alloc_finalizer(sizeof(hlext_result)); 15 | 16 | hl_buffer *b = hl_alloc_buffer(); 17 | hl_buffer_str(b, USTR("Egun on, ")); 18 | hl_buffer_str(b, (uchar*)name); 19 | hl_buffer_str(b, USTR("!")); 20 | 21 | cr->greeting = (vbyte*)hl_buffer_content(b, NULL); 22 | cr->age = currentYear - birthYear; 23 | 24 | return cr; 25 | } 26 | 27 | HL_PRIM vdynamic *HL_NAME(get_haxe_object)( vbyte* name, int birthYear, int currentYear ) { 28 | hlext_result *cr = HL_NAME(request_result)(name, birthYear, currentYear); 29 | 30 | // allocate new dynamic object 31 | vdynamic *obj = (vdynamic*)hl_alloc_dynobj(); 32 | // set field called "age" of 'obj' object of type 'int' with the cr->age value 33 | // hl_hash_gen(hl_to_utf16("age"), true): get the numeric represenation of the field name 34 | hl_dyn_seti(obj, hl_hash_gen(hl_to_utf16("age"), true), &hlt_i32, cr->age); 35 | 36 | // set field called "name" of 'obj' object of type 'vbyte' with the cr->name value 37 | // hl_hash_gen(hl_to_utf16("greeting"), true): get the numeric represenation of the field name 38 | hl_dyn_setp(obj, hl_hash_gen(hl_to_utf16("greeting"), true), &hlt_bytes, cr->greeting); 39 | 40 | return obj; 41 | } 42 | 43 | HL_PRIM vbyte *HL_NAME(result_greeting)( hlext_result* cr) { 44 | return cr->greeting; 45 | } 46 | 47 | HL_PRIM int HL_NAME(result_age)( hlext_result* cr) { 48 | return cr->age; 49 | } 50 | 51 | #define _RESULT _ABSTRACT( hlext_result ) 52 | 53 | 54 | DEFINE_PRIM(_RESULT, request_result, _BYTES _I32 _I32); 55 | DEFINE_PRIM(_DYN, get_haxe_object, _BYTES _I32 _I32); 56 | DEFINE_PRIM(_BYTES, result_greeting, _RESULT); 57 | DEFINE_PRIM(_I32, result_age, _RESULT); 58 | -------------------------------------------------------------------------------- /hlextLib/hlext/HLExt.hx: -------------------------------------------------------------------------------- 1 | package hlext; 2 | 3 | abstract Dyn(Dynamic) from T to T {} 4 | 5 | typedef HLExtResult = { 6 | var greeting:HString; 7 | var age:Int; 8 | } 9 | 10 | @:access(String) 11 | @:forward 12 | abstract HString(String) from String to String { 13 | @:from static public inline function fromBytes(b:hl.Bytes):HString 14 | return switch b { 15 | case null: null; 16 | default: String.fromUCS2(b); 17 | } 18 | 19 | @:to public inline function toBytes():hl.Bytes 20 | return switch this { 21 | case null: null; 22 | default: this.bytes; 23 | } 24 | } 25 | 26 | private typedef HLExtResultHandler = hl.Abstract<"hlext_result">; 27 | @:hlNative("hlext") 28 | private class CLib 29 | { 30 | public static function requestResult( name:hl.Bytes, birthYear:Int, currentYear:Int ) : HLExtResultHandler { return null; } 31 | public static function resultGreeting( result:HLExtResultHandler ) : Null { return null; } 32 | public static function resultAge( result:HLExtResultHandler ) : Int { return 0; } 33 | public static function getHaxeObject( name:hl.Bytes, birthYear:Int, currentYear:Int ) : Dyn<{greeting:hl.Bytes, age:Int}> { return null; } 34 | } 35 | 36 | class HLExt { 37 | 38 | static var result:HLExtResultHandler; 39 | 40 | public function new(name : HString, birthYear : Int, currentYear : Int) { 41 | result = CLib.requestResult(name, birthYear, currentYear); 42 | } 43 | 44 | public function getGreeting():HString { 45 | if (result == null) return ''; 46 | 47 | return CLib.resultGreeting(result); 48 | } 49 | 50 | public function getAge() { 51 | if (result == null) return 0; 52 | 53 | return CLib.resultAge(result); 54 | } 55 | 56 | static public function getHaxeObject( name : HString, birthYear : Int, currentYear : Int ) : HLExtResult { 57 | var r:{greeting:hl.Bytes, age:Int} = CLib.getHaxeObject(name, birthYear, currentYear); 58 | var hlextResult:HLExtResult = {greeting : r.greeting, age : r.age}; 59 | return hlextResult; 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /Dockerfile.win: -------------------------------------------------------------------------------- 1 | FROM haxe:4.0.5-windowsservercore-1809 2 | 3 | SHELL ["powershell", "-command"] 4 | 5 | ENV HASHLINK_BIN C:\\hashlink 6 | ENV HASHLINK C:\\hashlink\\include 7 | 8 | # PATH isn't actually set in the Docker image, so we have to set it from within the container 9 | RUN $newPath = ('{0};{1};{2};' -f $env:HASHLINK_BIN, $env:HASHLINK, $env:PATH); \ 10 | Write-Host ('Updating PATH: {0}' -f $newPath); \ 11 | [Environment]::SetEnvironmentVariable('PATH', $newPath, [EnvironmentVariableTarget]::Machine); \ 12 | $newInclude = ('{0}' -f $env:MSVC_INCLUDE); \ 13 | Write-Host ('Updating INCLUDE: {0}' -f $newInclude); \ 14 | [Environment]::SetEnvironmentVariable('INCLUDE', $newInclude, [EnvironmentVariableTarget]::Machine); \ 15 | $newLib = ('{0}' -f $env:MSVC_LIB); \ 16 | Write-Host ('Updating LIB: {0}' -f $newLib); \ 17 | [Environment]::SetEnvironmentVariable('LIB', $newLib, [EnvironmentVariableTarget]::Machine); \ 18 | $newLibPath = ('{0}' -f $env:MSVC_LIBPATH); \ 19 | Write-Host ('Updating LIBPATH: {0}' -f $newLibPath); \ 20 | [Environment]::SetEnvironmentVariable('LIBPATH', $newLibPath, [EnvironmentVariableTarget]::Machine); 21 | # doing this first to share cache across versions more aggressively 22 | 23 | RUN Invoke-WebRequest https://chocolatey.org/install.ps1 -UseBasicParsing | Invoke-Expression 24 | RUN choco install vcbuildtools -y 25 | RUN refreshenv 26 | 27 | RUN Write-Host 'Configuring environment'; \ 28 | pushd 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC' ; \ 29 | cmd /c 'vcvarsall.bat amd64&set' | foreach { \ 30 | if ($_ -match '=') { \ 31 | $v = $_.split('='); \ 32 | [Environment]::SetEnvironmentVariable($v[0], $v[1], [EnvironmentVariableTarget]::Machine); \ 33 | } \ 34 | } ; \ 35 | popd 36 | 37 | # install hashlink 38 | ENV HL_VERSION 1.10 39 | RUN $url = 'https://github.com/HaxeFoundation/hashlink/releases/download/1.11/hl-1.11.0-win.zip'; \ 40 | Write-Host ('Downloading {0} ...' -f $url); \ 41 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; \ 42 | Invoke-WebRequest -Uri $url -OutFile hashlink.zip; \ 43 | \ 44 | Write-Host 'Expanding ...'; \ 45 | New-Item -ItemType directory -Path tmp; \ 46 | Expand-Archive -Path hashlink.zip -DestinationPath tmp; \ 47 | if (Test-Path tmp\hl.exe) { Move-Item tmp $env:HASHLINK_BIN } \ 48 | else { Move-Item (Resolve-Path tmp\hl* | Select -ExpandProperty Path) $env:HASHLINK_BIN }; \ 49 | \ 50 | Write-Host 'Removing ...'; \ 51 | Remove-Item -Path hashlink.zip, tmp -Force -Recurse -ErrorAction Ignore; \ 52 | \ 53 | Write-Host 'Verifying install ...'; \ 54 | Write-Host ' hl'; hl; \ 55 | \ 56 | Write-Host 'Complete.'; 57 | 58 | CMD ["ping", "-t", "127.0.0.1"] 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HashLink Extension 2 | 3 | This library is created to learn how the bridge between Haxe and C works. It only has few functions: 4 | 5 | * HLExt.new(name:String, birthYear:Int, currentYear:Int): Creates an `struct` in C side with given arguments, returns that struct and we save it in the Haxe side. 6 | 7 | ```c 8 | struct _result { 9 | vbyte *greeting; 10 | int age; 11 | }; 12 | ``` 13 | 14 | * HLExt.getGreeting(): Get the name from C struct. 15 | * HLExt.getAge(): Get the age from C struct. 16 | * static HLExt.getHaxeObject(name:String, birthYear:Int, currentYear:Int): returns a Haxe object instead of a pointer to C side. 17 | 18 | ## Compiling HDLL file 19 | 20 | ### With Docker 21 | 22 | There are two Docker images (Windows and Linux) with the tools required for the compilation. Run the docker container for the desired OS and attach to it; 23 | 24 | * ```docker-compose up --build linux``` -> Attach to the container ```docker run -it [container_id] /bin/sh``` 25 | * ```docker-compose up --build win``` -> Attach to the container ```docker run -it [container_id] powershell``` 26 | 27 | Go to the ```makefile.*``` path and execute: 28 | 29 | * linux | mac: ```make -f makefile.[linux|mac]``` 30 | * move the newly created ```hlext.hdll``` to the path where ```libhl.so``` is, usualy ```/usr/local/lib``` 31 | * win: ```nmake -f makefile.win``` 32 | * rename ```hlext.dll``` to ```hlext.hdll``` and move it to the path where ```libhl.lib``` is. 33 | 34 | ### With MSYS2 on Windows 35 | 36 | With MSYS2 UCRT64 on Windows and with Hashlink installed in /c/HaxeToolkit/hl-1.11.0-win, from project's root: 37 | 38 | ```sh 39 | cd hlextLib 40 | gcc -O2 -shared -o hlext.hdll -std=c11 hlext.c -I/c/HaxeToolkit/hl-1.11.0-win/include -L /c/HaxeToolkit/hl-1.11.0-win/ -lhl -llibhl 41 | ``` 42 | 43 | Move ```hlext.hdll``` to the path where ```libhl.lib``` is. 44 | 45 | Now from your IDE at project's root: 46 | 47 | ```sh 48 | haxe .\build.hxml 49 | hl .\main.hl 50 | ``` 51 | 52 | ## Commands breakdown 53 | 54 | ### Linux and mac 55 | 56 | ```shell 57 | gcc -O3 -shared -o hlext.hdll -std=c11 hlext.c -I/usr/local/include -lhl 58 | ``` 59 | 60 | * **gcc**: compiler call 61 | * **-O3**: level 3 optimization 62 | * **-shared**: flag to tell this is a shared library 63 | * **-o file.out**: define the output file 64 | * **-std=standard**: C language standard 65 | * **hlextLib/hlext.c**: C file to compile 66 | * **-Idirectory**: Include the directory where headers files are. In this case only `hl.h` is needed 67 | * **-lname**: Use `libname` library 68 | * **-fPIC**: Position Independent Code 69 | 70 | ### Windows 71 | 72 | TODO 73 | 74 | ## Usage 75 | 76 | See `Main.hx`. 77 | 78 | ## TODO 79 | 80 | * Try compiling with HLC. 81 | * Add a callback to C side and then call to Haxe from C. 82 | --------------------------------------------------------------------------------