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