b40ecd3c23545040484f0ed8bb560f085ce3f7a9
[vg.git] / vg_build.h
1 /* zig cc scripting tools */
2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <time.h>
7 #include <stdarg.h>
8
9 #include "vg_opt.h"
10 #include "vg_log.h"
11 #include "vg_string.h"
12 #include "vg_build_font.h"
13
14 /* we dont free dynamic vg_strs in this program. so, we dont care.. */
15 const char *__asan_default_options() { return "detect_leaks=0"; }
16
17 struct vg_env
18 {
19 u32 optimization;
20
21 bool fresh,
22 debug_asan;
23
24 enum platform
25 {
26 k_platform_anyplatform,
27 k_platform_windows,
28 k_platform_linux
29 }
30 platform;
31
32 enum architecture
33 {
34 k_architecture_anyarch,
35 k_architecture_i386,
36 k_architecture_x86_64,
37 }
38 arch;
39
40 enum compiler
41 {
42 k_compiler_blob,
43 k_compiler_clang,
44 k_compiler_zigcc
45 }
46 compiler;
47
48 enum libc_version
49 {
50 k_libc_version_native,
51 k_libc_version_2_23,
52 }
53 libc;
54 };
55
56 struct vg_env vg_test_env = {
57 .arch = k_architecture_x86_64,
58 .compiler = k_compiler_clang,
59 .libc = k_libc_version_native,
60 .debug_asan = 1,
61 .fresh = 0,
62 .platform = k_platform_linux,
63 .optimization = 0
64 };
65
66 struct vg_env vg_release_env = {
67 .arch = k_architecture_x86_64,
68 .compiler = k_compiler_zigcc,
69 .libc = k_libc_version_2_23,
70 .fresh = 1,
71 .optimization = 3,
72 .platform = k_platform_anyplatform,
73 .debug_asan = 0
74 };
75
76 struct vg_project
77 {
78 struct vg_env *env;
79
80 vg_str include, /* -I<path> */
81 library, /* -L<path> */
82 link, /* -llibrary */
83 sources, /* file.c obj.o */
84 uid, /* env/project identifier */
85 target, /* result object name */
86
87 /* generated */
88 compiled_objects; /* space seperated paths to compiled objects */
89
90 enum obj_type {
91 k_obj_type_none,
92 k_obj_type_exe,
93 k_obj_type_obj,
94 k_obj_type_shared,
95 }
96 type;
97 };
98
99 /*
100 * string tables
101 * -------------------------------------------------------------------------- */
102
103 static const char *platform_names[] =
104 {
105 [k_platform_anyplatform] = "anyplatform",
106 [k_platform_windows] = "windows",
107 [k_platform_linux] = "linux"
108 };
109
110 static const char *architecture_names[] =
111 {
112 [k_architecture_anyarch] = "anyarch",
113 [k_architecture_i386] = "i386",
114 [k_architecture_x86_64] = "x86_64"
115 };
116
117 static const char *compiler_names[] =
118 {
119 [k_compiler_blob] = "blob",
120 [k_compiler_clang] = "clang",
121 [k_compiler_zigcc] = "zig-cc"
122 };
123
124 static const char *compiler_paths[] =
125 {
126 [k_compiler_blob] = NULL,
127 [k_compiler_clang] = "clang",
128 [k_compiler_zigcc] = "zig cc"
129 };
130
131 static const char *libc_names[] =
132 {
133 [k_libc_version_native] = "",
134 [k_libc_version_2_23] = ".2.23"
135 };
136
137 /*
138 * source specification
139 * -------------------------------------------------------------------------- */
140
141 void vg_add_source( struct vg_project *proj, const char *source )
142 {
143 if( proj->type == k_obj_type_none )
144 vg_fatal_error( "Cannot add source code without setting binary type\n" );
145
146 vg_strcat( &proj->sources, source );
147 vg_strcat( &proj->sources, " " );
148 }
149
150 void vg_include_dir( struct vg_project *proj, const char *dir )
151 {
152 if( proj->type == k_obj_type_none )
153 vg_fatal_error( "Cannot add include dir without setting binary type\n" );
154
155 vg_strcat( &proj->include, dir );
156 vg_strcat( &proj->include, " " );
157 }
158
159 void vg_library_dir( struct vg_project *proj, const char *dir )
160 {
161 if( proj->type == k_obj_type_none )
162 vg_fatal_error( "Cannot add library dir without setting binary type\n" );
163
164 vg_strcat( &proj->library, dir );
165 vg_strcat( &proj->library, " " );
166 }
167
168 void vg_link( struct vg_project *proj, const char *lib )
169 {
170 if( proj->type == k_obj_type_none )
171 vg_fatal_error( "Cannot link library without setting binary type\n" );
172
173 vg_strcat( &proj->link, lib );
174 }
175
176 /*
177 * OS & file tools
178 * -------------------------------------------------------------------------- */
179
180 void vg_syscall( const char *fmt, ... )
181 {
182 va_list args;
183 va_start( args, fmt );
184
185 char call[4096];
186 vsnprintf( call, sizeof(call), fmt, args );
187
188 va_end( args );
189 vg_low( "%s\n", call );
190 if( system(call) )
191 exit(1);
192 }
193
194 void vg_add_blob( struct vg_project *proj, const char *blob, const char *dest )
195 {
196 vg_syscall( "cp %s bin/%s/%s", blob, proj->uid.buffer, dest );
197 }
198
199 void vg_symlink( struct vg_project *proj,
200 const char *folder, const char *bin_name )
201 {
202 char dest[512];
203 snprintf( dest, 512, "bin/%s/%s", proj->uid.buffer, bin_name );
204 if( !access( dest, F_OK ) )
205 vg_syscall( "unlink %s", dest );
206 vg_syscall( "ln -srf %s %s", folder, dest );
207 }
208
209 void vg_tarball_project( struct vg_project *proj )
210 {
211 vg_syscall( "tar -chzvf dist/%s-%u.tar.gz bin/%s/",
212 proj->uid.buffer, time(NULL), proj->uid.buffer );
213 }
214
215 /*
216 * The project configurator and compiler.
217 * -------------------------------------------------------------------------- */
218
219 void vg_project_new_target( struct vg_project *proj, const char *name,
220 enum obj_type type )
221 {
222 proj->type = type;
223
224 vg_strnull( &proj->include, NULL, -1 );
225 vg_strnull( &proj->library, NULL, -1 );
226 vg_strnull( &proj->link, NULL, -1 );
227 vg_strnull( &proj->sources, NULL, -1 );
228 vg_strnull( &proj->target, NULL, -1 );
229
230 /*
231 * Setup target with appropriate extension
232 */
233 vg_strcat( &proj->target, name );
234
235 if( proj->env->platform == k_platform_windows )
236 {
237 if( type == k_obj_type_exe )
238 vg_strcat( &proj->target, ".exe" );
239 else if( type == k_obj_type_shared )
240 vg_strcat( &proj->target, ".dll" );
241 else if( type == k_obj_type_obj )
242 vg_strcat( &proj->target, ".obj" );
243 }
244
245
246 if( proj->env->platform == k_platform_linux )
247 {
248 if( type == k_obj_type_shared )
249 vg_strcat( &proj->target, ".so" );
250 else if( type == k_obj_type_obj )
251 vg_strcat( &proj->target, ".o" );
252 }
253
254 /*
255 * Add some regular includes / library dirs
256 */
257 if( type != k_obj_type_none )
258 {
259 vg_include_dir( proj, "-I." );
260 vg_include_dir( proj, "-I./vg" );
261 vg_library_dir( proj, "-L." );
262 vg_library_dir( proj, "-L/usr/lib" );
263 }
264
265 vg_info( " New target: %s\n", name );
266 }
267
268 void vg_project_init( struct vg_project *proj,
269 struct vg_env *env,
270 const char *identifier )
271 {
272 proj->env = env;
273 proj->type = k_obj_type_none;
274
275 vg_strnull( &proj->uid, NULL, -1 );
276 vg_strnull( &proj->compiled_objects, NULL, -1 );
277
278 /* check for problems in configuration */
279 if( env->libc != k_libc_version_native ){
280 if( env->compiler != k_compiler_zigcc ){
281 vg_fatal_error(
282 "Cannot specify libc version using the '%s' compiler.\n",
283 compiler_names[ env->compiler ] );
284 }
285 }
286
287 if( env->compiler == k_compiler_clang ){
288 if( env->platform != k_platform_linux ){
289 vg_fatal_error( "Cannot compile for '%s' using the '%s' compiler;" );
290 }
291 }
292
293 vg_strcat( &proj->uid, identifier );
294 vg_strcatch( &proj->uid, '-' );
295 vg_strcat( &proj->uid, platform_names[ env->platform ] );
296 vg_strcatch( &proj->uid, '-' );
297 vg_strcat( &proj->uid, architecture_names[ env->arch ] );
298 vg_strcatch( &proj->uid, '-' );
299 vg_strcat( &proj->uid, compiler_names[ env->compiler ] );
300
301 if( proj->uid.i < 3 )
302 vg_fatal_error( "failed to create project UID\n" );
303
304 vg_info( "project_init: %s (%s, %s, compiler: %s, opt:%u, fresh: %s)\n",
305 identifier,
306 platform_names[env->platform],
307 architecture_names[env->arch],
308 compiler_names[env->compiler],
309 env->optimization,
310 env->fresh? "yes":"no");
311
312 if( env->fresh )
313 vg_syscall( "rm -rf bin/%s", proj->uid.buffer );
314 vg_syscall( "mkdir -p bin/%s", proj->uid.buffer );
315 }
316
317 void vg_compile_project( struct vg_project *proj )
318 {
319 vg_str cmd;
320 vg_strnull( &cmd, NULL, -1 );
321
322 /* compiler specification */
323 vg_strcat( &cmd, "ccache " );
324 vg_strcat( &cmd, compiler_paths[ proj->env->compiler ] );
325 vg_strcat( &cmd, " -std=gnu99 -D_REENTRANT \\\n" );
326
327 if( proj->env->optimization )
328 {
329 vg_strcat( &cmd, " -O" );
330 vg_strcati32( &cmd, proj->env->optimization );
331 vg_strcat( &cmd, " -flto \\\n" );
332 }
333 else
334 {
335 /* add debugger / asan information */
336 vg_strcat( &cmd, " -O0 -ggdb3 -fno-omit-frame-pointer " );
337
338 if( (proj->env->compiler == k_compiler_clang) && proj->env->debug_asan )
339 {
340 vg_strcat( &cmd, " -rdynamic -fsanitize=address -fPIE "
341 "-fstack-protector-strong " );
342 }
343
344 vg_strcat( &cmd, "\\\n" );
345 }
346
347 /* want a lot of warnings but not useless ones */
348 vg_strcat( &cmd, " -Wall -ferror-limit=8\\\n"
349 " -Wno-unused-function -Wno-unused-variable\\\n"
350 " -Wno-unused-command-line-argument -Wno-unused-but-set-variable\\\n"
351 );
352
353 if( proj->env->compiler != k_compiler_clang )
354 vg_strcat( &cmd, " -Wno-format-truncation\\\n" );
355
356 /* include paths */
357 vg_strcat( &cmd, " " );
358 vg_strcat( &cmd, proj->include.buffer );
359 vg_strcat( &cmd, "\\\n" );
360
361 /* library paths */
362 vg_strcat( &cmd, " " );
363 vg_strcat( &cmd, proj->library.buffer );
364 vg_strcat( &cmd, "\\\n" );
365
366 /* sources */
367 vg_strcat( &cmd, " " );
368
369 if( proj->type == k_obj_type_obj )
370 vg_strcat( &cmd, "-c " );
371
372 if( proj->type == k_obj_type_shared )
373 vg_strcat( &cmd, "-shared -fPIC " );
374
375 vg_strcat( &cmd, proj->sources.buffer );
376 vg_strcat( &cmd, "\\\n" );
377
378 /* output */
379 vg_strcat( &cmd, " -o bin/" );
380 vg_strcat( &cmd, proj->uid.buffer );
381 vg_strcat( &cmd, "/" );
382 vg_strcat( &cmd, proj->target.buffer );
383 vg_strcat( &cmd, "\\\n" );
384
385 /* link */
386 vg_strcat( &cmd, " " );
387 vg_strcat( &cmd, proj->link.buffer );
388 vg_strcat( &cmd, "\\\n" );
389
390 if( proj->type == k_obj_type_exe )
391 {
392 vg_strcat( &cmd, " -Wl,-rpath=./\\\n" );
393 }
394
395 /* target platform specification (zig-cc only) */
396 if( proj->env->compiler == k_compiler_zigcc ){
397 vg_strcat( &cmd, " -target " );
398 vg_strcat( &cmd, architecture_names[proj->env->arch] );
399 vg_strcat( &cmd, "-" );
400 vg_strcat( &cmd, platform_names[proj->env->platform] );
401
402 if( proj->env->platform == k_platform_linux ){
403 vg_strcat( &cmd, "-gnu" );
404 vg_strcat( &cmd, libc_names[proj->env->libc] );
405 }
406
407 if( proj->env->platform == k_platform_windows )
408 {
409 /* we currently dont want pdb pretty much ever. goodbye! */
410
411 if( proj->type == k_obj_type_exe )
412 {
413 vg_strcat( &cmd, " /pdb:/dev/null" );
414 vg_strcat( &cmd, " /SUBSYSTEM:windows" );
415 }
416 }
417 }
418
419 vg_syscall( cmd.buffer );
420
421 /* add to results */
422 vg_strcat( &proj->compiled_objects, "bin/" );
423 vg_strcat( &proj->compiled_objects, proj->uid.buffer );
424 vg_strcat( &proj->compiled_objects, "/" );
425 vg_strcat( &proj->compiled_objects, proj->target.buffer );
426 vg_strcat( &proj->compiled_objects, " \\\n " );
427 }
428
429 /*
430 * Standard VG includes & libraries which we use for games/graphics
431 * -------------------------------------------------------------------------- */
432
433 struct vg_engine_config
434 {
435 bool use_3d, legacy_support_vg_msg1, log_source_info, steam_api,
436 custom_game_settings,
437 custom_shaders;
438 i32 fixed_update_hz;
439 }
440 vg_engine_default_config = {
441 .use_3d = 1,
442 .fixed_update_hz = 60,
443 .legacy_support_vg_msg1 = 0,
444 .log_source_info = 1,
445 .steam_api = 0,
446 .custom_game_settings = 0,
447 .custom_shaders = 0
448 };
449
450 void vg_add_engine( struct vg_project *proj, struct vg_engine_config *config )
451 {
452 /* building assets */
453 vg_build_default_font();
454
455 if( !config ) config = &vg_engine_default_config;
456 vg_str config_string;
457 vg_strnull( &config_string, NULL, -1 );
458 vg_strcat( &config_string, config->use_3d? "-DVG_3D \\\n": "-DVG_2D \\\n" );
459 vg_strcat( &config_string, "-DVG_TIMESTEP_FIXED=\"(1.0/" );
460 vg_strcati32( &config_string, config->fixed_update_hz );
461 vg_strcat( &config_string, ".0)\" \\\n" );
462 if( config->legacy_support_vg_msg1 )
463 vg_strcat( &config_string, "-DVG_MSG_V1_SUPPORT \\\n" );
464 if( config->log_source_info )
465 vg_strcat( &config_string, "-DVG_LOG_SOURCE_INFO \\\n" );
466 if( config->custom_game_settings )
467 vg_strcat( &config_string, "-DVG_GAME_SETTINGS \\\n" );
468 if( config->custom_shaders )
469 vg_strcat( &config_string, "-DVG_CUSTOM_SHADERS \\\n" );
470
471 vg_strcat( &config_string, "\\\n" );
472
473 /* compile heavy dependencies seperately */
474 struct vg_project dep_proj;
475 struct vg_env env = *proj->env;
476 env.optimization = 3;
477 env.debug_asan = 0;
478
479 vg_project_init( &dep_proj, proj->env, "vg" );
480
481 /* external dependencies */
482 vg_project_new_target( &dep_proj, "vg_deps", k_obj_type_obj );
483 vg_add_source( &dep_proj, "vg/vg_depencies.c" );
484 vg_compile_project( &dep_proj );
485
486 /* glad */
487 vg_project_new_target( &dep_proj, "vg_glad", k_obj_type_obj );
488 vg_add_source( &dep_proj, "vg/dep/glad/glad.c" );
489 vg_include_dir( &dep_proj, "-I./vg/dep " );
490 vg_compile_project( &dep_proj );
491
492 /* core engine */
493 vg_project_new_target( &dep_proj, "vg_engine_core", k_obj_type_obj );
494 vg_add_source( &dep_proj, config_string.buffer );
495 vg_add_source( &dep_proj, "vg/vg_engine.c" );
496 vg_include_dir( &dep_proj, "-I./vg/dep " );
497 vg_compile_project( &dep_proj );
498
499 /* steamworks */
500 if( config->steam_api )
501 {
502 vg_project_new_target( &dep_proj, "vg_steam", k_obj_type_obj );
503 vg_add_source( &dep_proj, "vg/vg_steam.c" );
504 vg_compile_project( &dep_proj );
505
506 if( proj->env->platform == k_platform_linux )
507 {
508 vg_add_blob( proj, "vg/dep/steam/libsteam_api.so", "" );
509 vg_link( proj, "-lsteam_api " );
510 }
511 else if( proj->env->platform == k_platform_windows )
512 {
513 vg_add_blob( proj, "vg/dep/steam/steam_api64.dll", "" );
514 vg_link( proj, "vg/dep/steam/steam_api64.dll " );
515 }
516
517 vg_library_dir( proj, "-L./vg/dep/steam " );
518 vg_include_dir( proj, "-I./vg/dep " );
519 }
520
521 /* precipitate to the client project */
522
523 vg_link( proj, "-lm " );
524 if( proj->env->platform == k_platform_linux )
525 {
526 vg_link( proj, "-lSDL2 -lGL -lX11 -lXxf86vm "
527 "-lXrandr -lXi -ldl -pthread " );
528 }
529 else if( proj->env->platform == k_platform_windows )
530 {
531 vg_link( proj, "-lSDL2main -lSDL2 -lopengl32 \\\n" );
532 vg_link( proj, "vg/dep/sdl/SDL2.dll " );
533 vg_add_blob( proj, "vg/dep/sdl/SDL2.dll ", "" );
534 vg_library_dir( proj, "-L./vg/dep/sdl " );
535 }
536
537 vg_add_source( proj, config_string.buffer );
538 vg_add_source( proj, dep_proj.compiled_objects.buffer );
539 vg_add_source( proj, "\\\n" );
540 vg_include_dir( proj, "-I./vg/dep " );
541 vg_link( proj, "-lm " );
542 }
543
544 void vg_add_controller_database( struct vg_project *proj )
545 {
546 vg_add_blob( proj,
547 "vg/submodules/SDL_GameControllerDB/gamecontrollerdb.txt", "" );
548 }