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+
1168class 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