Skip to content

Commit 41160d9

Browse files
Add ninja generator.
1 parent 0f3bc39 commit 41160d9

File tree

4 files changed

+197
-11
lines changed

4 files changed

+197
-11
lines changed

.github/workflows/build.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ jobs:
124124
libturbojpeg-dev \
125125
libuv1-dev \
126126
libvorbis-dev \
127-
libsqlite3-dev
127+
libsqlite3-dev \
128+
ninja-build
128129
;;
129130
130131
darwin*)
@@ -152,7 +153,7 @@ jobs:
152153
set -eux
153154
154155
haxelib setup ~/haxelib
155-
haxelib install hashlink
156+
haxelib dev hashlink other/haxelib
156157
haxelib list
157158
158159
@@ -233,6 +234,11 @@ jobs:
233234
haxe -hl src/_main.c -cp other/tests -main HelloWorld
234235
make hlc
235236
./hlc
237+
haxe -hl out/helloworld/main.c -cp other/tests -main HelloWorld -D hlgen.makefile=ninja
238+
out/helloworld/main
239+
240+
haxe -hl out/fmtsample/main.c -cp other/fmtsample -main FmtSample -D hlgen.makefile=ninja
241+
out/fmtsample/main
236242
;;
237243
esac
238244

Brewfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ brew "mbedtls"
1111
brew "libuv"
1212
brew "openssl"
1313
brew "sqlite"
14+
brew "ninja"

other/fmtsample/FmtSample.hx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
function main() {
2+
var out = haxe.io.Bytes.alloc(16);
3+
var src = haxe.io.Bytes.ofString("Hello World!");
4+
hl.Format.digest(out.getData(), src.getData(), src.length, 0 /* md5 */);
5+
final expected = "ed076287532e86365e841e92bfc50d8c";
6+
final got = out.toHex();
7+
if (got != expected) {
8+
throw 'expected $expected, got $got';
9+
}
10+
}

other/haxelib/Run.hx

Lines changed: 178 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,178 @@
1+
import sys.io.File;
2+
import haxe.io.Path;
3+
using StringTools;
4+
5+
class NinjaGenerator {
6+
var buf:StringBuf;
7+
8+
public function new() {
9+
buf = new StringBuf();
10+
}
11+
12+
function comment(value:String, empty_line = false) {
13+
buf.add('# $value \n');
14+
if (empty_line) buf.add('\n');
15+
}
16+
17+
function bind(name:String, value:String) {
18+
buf.add('$name = $value\n\n');
19+
}
20+
21+
function rule(name:String, args:Map<String, String>) {
22+
buf.add('rule $name\n');
23+
for (key => value in args) {
24+
buf.add(' $key = $value\n');
25+
}
26+
buf.add('\n');
27+
}
28+
29+
function build(out:Array<String>, rule:String, input:Array<String>, ?args:Map<String, String>) {
30+
if(args == null) args = [];
31+
buf.add('build ${out.join(' ')}: $rule ${input.join(' ')}\n');
32+
for (key => value in args) {
33+
buf.add(' $key = $value\n');
34+
}
35+
buf.add('\n');
36+
}
37+
38+
function save(path:String) {
39+
var str = this.buf.toString();
40+
File.saveContent(path, str);
41+
}
42+
43+
public static function gen(config: HlcConfig, output: String) {
44+
var gen = new NinjaGenerator();
45+
gen.comment('Automatically generated file, do not edit', true);
46+
gen.bind('ninja_required_version', '1.2');
47+
48+
var compiler_flavor: CCFlavor = switch Sys.systemName() {
49+
case "Windows": MSVC;
50+
case _: GCC;
51+
}
52+
53+
switch compiler_flavor {
54+
case GCC:
55+
var prefix = "/usr/local";
56+
if (Sys.systemName() == "Mac") {
57+
var proc = new sys.io.Process("brew", ["--prefix", "hashlink"]);
58+
proc.stdin.close();
59+
if (proc.exitCode(true) == 0) {
60+
var path = proc.stdout.readAll().toString().trim();
61+
if (sys.FileSystem.exists(path)) {
62+
prefix = path;
63+
}
64+
}
65+
}
66+
var opt_flag = config.defines.exists("debug") ? "-g" : '-O2';
67+
var rpath = switch Sys.systemName() {
68+
case "Mac": '-rpath @executable_path -rpath $prefix/lib';
69+
case _: '-Wl,-rpath,$$ORIGIN:$prefix/lib';
70+
};
71+
gen.bind('cflags', '$opt_flag -std=c11 -DHL_MAKE -Wall -I. -pthread');
72+
final libflags = config.libs.map((lib) -> switch lib {
73+
case "std": "-lhl";
74+
case "uv": '$prefix/lib/$lib.hdll -luv';
75+
case var lib: '$prefix/lib/$lib.hdll';
76+
}).join(' ');
77+
gen.bind('ldflags', '-pthread -lm -L$prefix/lib $libflags $rpath');
78+
gen.rule('cc', [
79+
"command" => "cc -MD -MF $out.d $cflags -c $in -o $out",
80+
"deps" => "gcc",
81+
"depfile" => "$out.d",
82+
]);
83+
gen.rule('ld', [
84+
"command" => "cc $in -o $out $ldflags"
85+
]);
86+
case MSVC:
87+
gen.bind('hashlink', Sys.getEnv('HASHLINK'));
88+
gen.bind('cflags', "/DHL_MAKE /std:c11 /I. /I$hashlink\\include");
89+
final libflags = config.libs.map((lib) -> switch lib {
90+
case "std": "libhl.lib";
91+
case var lib: '$lib.lib';
92+
});
93+
gen.bind('ldflags', "/LIBPATH:$hashlink " + libflags);
94+
gen.rule('cc', [
95+
"command" => "cl.exe /nologo /showIncludes $cflags /c $in /Fo$out",
96+
"deps" => "msvc",
97+
]);
98+
gen.rule('ld', [
99+
"command" => "link.exe /nologo /OUT:$out $ldflags @$out.rsp",
100+
"rspfile" => "$out.rsp",
101+
"rspfile_content" => "$in"
102+
]);
103+
}
104+
105+
final objects = [];
106+
107+
for (file in config.files) {
108+
final out_path = haxe.io.Path.withExtension(file, 'o');
109+
objects.push(out_path);
110+
gen.build([out_path.toString()], "cc", [file], []);
111+
}
112+
113+
final exe_path = Path.withExtension(Path.withoutDirectory(output), switch compiler_flavor {
114+
case MSVC: "exe";
115+
case GCC: null;
116+
});
117+
gen.build([exe_path], 'ld', objects, []);
118+
119+
gen.save(Path.join([Path.directory(output), 'build.ninja']));
120+
}
121+
122+
public static function run(dir:String) {
123+
switch Sys.systemName() {
124+
case "Windows":
125+
var devcmd = findVsDevCmdScript();
126+
var devcmd = haxe.SysTools.quoteWinArg(devcmd, true);
127+
Sys.command('$devcmd && ninja -C $dir');
128+
case _:
129+
Sys.command("ninja", ["-C", dir]);
130+
}
131+
}
132+
133+
private static function findVsDevCmdScript(): Null<String> {
134+
var proc = new sys.io.Process('C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe', [
135+
"-latest",
136+
"-products", "*",
137+
"-requires",
138+
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
139+
"-property",
140+
"installationPath"
141+
]);
142+
proc.stdin.close();
143+
var stdout = proc.stdout.readAll();
144+
if (proc.exitCode(true) == 0) {
145+
var instPath = stdout.toString().trim();
146+
return '$instPath\\VC\\Auxiliary\\Build\\vcvars64.bat';
147+
} else {
148+
return null;
149+
}
150+
}
151+
}
152+
153+
enum abstract CCFlavor(String) {
154+
var MSVC = "msvc";
155+
/**
156+
* GCC, Clang, etc
157+
**/
158+
var GCC = "gcc";
159+
}
160+
161+
typedef HlcConfig = {
162+
var version:Int;
163+
var libs:Array<String>;
164+
var defines:haxe.DynamicAccess<String>;
165+
var files:Array<String>;
166+
};
167+
1168
class Build {
2169

3170
var output : String;
4171
var name : String;
5172
var sourcesDir : String;
6173
var targetDir : String;
7174
var dataPath : String;
8-
var config : {
9-
var version : Int;
10-
var libs : Array<String>;
11-
var defines : haxe.DynamicAccess<String>;
12-
var files : Array<String>;
13-
};
175+
var config : HlcConfig;
14176

15177
public function new(dataPath,output,config) {
16178
this.output = output;
@@ -29,9 +191,14 @@ class Build {
29191

30192

31193
public function run() {
32-
var tpl = config.defines.get("hlgen.makefile");
33-
if( tpl != null )
34-
generateTemplates(tpl);
194+
switch config.defines.get("hlgen.makefile") {
195+
case "ninja":
196+
NinjaGenerator.gen(config, output);
197+
198+
case var tpl:
199+
if( tpl != null )
200+
generateTemplates(tpl);
201+
}
35202
log('Code generated in $output');
36203
switch tpl {
37204
case "make":
@@ -40,6 +207,8 @@ class Build {
40207
Sys.command("haxelib", ["--cwd", targetDir, "run", "hxcpp", "Build.xml"].concat(config.defines.exists("debug") ? ["-Ddebug"] : []));
41208
case "vs2019", "vs2022":
42209
Sys.command("make", ["-C", targetDir]);
210+
case "ninja":
211+
NinjaGenerator.run(Path.directory(output));
43212
case null:
44213
var suggestion = (Sys.systemName() == "Windows") ? "vs2019" : "make";
45214
log('Set -D hlgen.makefile=${suggestion} for automatic native compilation');

0 commit comments

Comments
 (0)