diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6abc6f76c..239d129bb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -127,7 +127,8 @@ jobs: libturbojpeg-dev \ libuv1-dev \ libvorbis-dev \ - libsqlite3-dev + libsqlite3-dev \ + ninja-build ;; darwin*) @@ -155,7 +156,7 @@ jobs: set -eux haxelib setup ~/haxelib - haxelib install hashlink + haxelib dev hashlink other/haxelib haxelib list @@ -233,9 +234,11 @@ jobs: popd fi - haxe -hl src/_main.c -cp other/tests -main HelloWorld - make hlc - ./hlc + haxe -hl out/helloworld/main.c -cp other/tests -main HelloWorld -D hlgen.makefile=ninja + out/helloworld/main + + haxe -hl out/fmtsample/main.c -cp other/fmtsample -main FmtSample -D hlgen.makefile=ninja + out/fmtsample/main ;; esac diff --git a/Brewfile b/Brewfile index 9fa2ce060..631cfaab9 100644 --- a/Brewfile +++ b/Brewfile @@ -11,3 +11,4 @@ brew "mbedtls" brew "libuv" brew "openssl" brew "sqlite" +brew "ninja" diff --git a/other/fmtsample/FmtSample.hx b/other/fmtsample/FmtSample.hx new file mode 100644 index 000000000..6939fbf74 --- /dev/null +++ b/other/fmtsample/FmtSample.hx @@ -0,0 +1,10 @@ +function main() { + var out = haxe.io.Bytes.alloc(16); + var src = haxe.io.Bytes.ofString("Hello World!"); + hl.Format.digest(out.getData(), src.getData(), src.length, 0 /* md5 */); + final expected = "ed076287532e86365e841e92bfc50d8c"; + final got = out.toHex(); + if (got != expected) { + throw 'expected $expected, got $got'; + } +} \ No newline at end of file diff --git a/other/haxelib/Run.hx b/other/haxelib/Run.hx index 6f0fe346f..03942eeb8 100644 --- a/other/haxelib/Run.hx +++ b/other/haxelib/Run.hx @@ -1,15 +1,161 @@ +import sys.io.File; +import haxe.io.Path; +using StringTools; + +class NinjaGenerator { + var buf:StringBuf; + + public function new() { + buf = new StringBuf(); + } + + function comment(value:String, empty_line = false) { + buf.add('# $value \n'); + if (empty_line) buf.add('\n'); + } + + function bind(name:String, value:String) { + buf.add('$name = $value\n\n'); + } + + function rule(name:String, args:Map) { + buf.add('rule $name\n'); + for (key => value in args) { + buf.add(' $key = $value\n'); + } + buf.add('\n'); + } + + function build(out:Array, rule:String, input:Array, ?args:Map) { + if(args == null) args = []; + buf.add('build ${out.join(' ')}: $rule ${input.join(' ')}\n'); + for (key => value in args) { + buf.add(' $key = $value\n'); + } + buf.add('\n'); + } + + function save(path:String) { + var str = this.buf.toString(); + File.saveContent(path, str); + } + + public static function gen(config: HlcConfig, output: String) { + var gen = new NinjaGenerator(); + gen.comment('Automatically generated file, do not edit', true); + gen.bind('ninja_required_version', '1.2'); + + var compiler_flavor: CCFlavor = switch Sys.systemName() { + case "Windows": MSVC; + case _: GCC; + } + + switch compiler_flavor { + case GCC: + var opt_flag = config.defines.exists("debug") ? "-g" : '-O2'; + var rpath = switch Sys.systemName() { + case "Mac": "-rpath @executable_path -rpath /usr/local/lib"; + case _: "-Wl,-rpath,$$ORIGIN:/usr/local/lib"; + }; + gen.bind('cflags', '$opt_flag -std=c11 -DHL_MAKE -Wall -I. -pthread'); + final libflags = config.libs.map((lib) -> switch lib { + case "std": "-lhl"; + case "uv": '/usr/local/lib/$lib.hdll -luv'; + case var lib: '/usr/local/lib/$lib.hdll'; + }).join(' '); + gen.bind('ldflags', '-pthread -lm -L/usr/local/lib $libflags $rpath'); + gen.rule('cc', [ + "command" => "cc -MD -MF $out.d $cflags -c $in -o $out", + "deps" => "gcc", + "depfile" => "$out.d", + ]); + gen.rule('ld', [ + "command" => "cc $in -o $out $ldflags" + ]); + case MSVC: + gen.bind('hashlink', Sys.getEnv('HASHLINK')); + gen.bind('cflags', "/DHL_MAKE /std:c11 /I. /I$hashlink\\include"); + gen.bind('ldflags', "/LIBPATH:$hashlink libhl.lib"); + gen.rule('cc', [ + "command" => "cl.exe /nologo /showIncludes $cflags /c $in /Fo$out", + "deps" => "msvc", + ]); + gen.rule('ld', [ + "command" => "link.exe /nologo /OUT:$out $ldflags @$out.rsp", + "rspfile" => "$out.rsp", + "rspfile_content" => "$in" + ]); + } + + final objects = []; + + for (file in config.files) { + final out_path = haxe.io.Path.withExtension(file, 'o'); + objects.push(out_path); + gen.build([out_path.toString()], "cc", [file], []); + } + + final exe_path = Path.withExtension(Path.withoutDirectory(output), switch compiler_flavor { + case MSVC: "exe"; + case GCC: null; + }); + gen.build([exe_path], 'ld', objects, []); + + gen.save(Path.join([Path.directory(output), 'build.ninja'])); + } + + public static function run(dir:String) { + switch Sys.systemName() { + case "Windows": + var devcmd = findVsDevCmdScript(); + Sys.command("cmd.exe", ["/C", devcmd, "-arch=x64", '&&', 'ninja', '-C', dir]); + case _: + Sys.command("ninja", ["-C", dir]); + } + } + + private static function findVsDevCmdScript(): Null { + var proc = new sys.io.Process('C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe', [ + "-latest", + "-products", "*", + "-requires", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", + "installationPath" + ]); + proc.stdin.close(); + var stdout = proc.stdout.readAll(); + if (proc.exitCode(true) == 0) { + var instPath = stdout.toString().trim(); + return '$instPath\\Common7\\Tools\\vsdevcmd.bat'; + } else { + return null; + } + } +} + +enum abstract CCFlavor(String) { + var MSVC = "msvc"; + /** + * GCC, Clang, etc + **/ + var GCC = "gcc"; +} + +typedef HlcConfig = { + var version:Int; + var libs:Array; + var defines:haxe.DynamicAccess; + var files:Array; +}; + class Build { var output : String; var name : String; var targetDir : String; var dataPath : String; - var config : { - var version : Int; - var libs : Array; - var defines : haxe.DynamicAccess; - var files : Array; - }; + var config : HlcConfig; public function new(dataPath,output,config) { this.output = output; @@ -21,11 +167,15 @@ class Build { } public function run() { - var tpl = config.defines.get("hlgen.makefile"); - if( tpl != null ) - generateTemplates(tpl); - if( config.defines.get("hlgen.silent") == null ) - Sys.println("Code generated in "+output+" automatic native compilation not yet implemented"); + switch config.defines.get("hlgen.makefile") { + case "ninja": + NinjaGenerator.gen(config, output); + NinjaGenerator.run(Path.directory(output)); + case var tpl: + generateTemplates(tpl); + if( config.defines.get("hlgen.silent") == null ) + Sys.println("Code generated in "+output+" automatic native compilation not yet implemented"); + } } function isAscii( bytes : haxe.io.Bytes ) {