camera_cnn

Unnamed repository; edit this file 'description' to name the repository.
git clone anongit@rnpnr.xyz:camera_cnn.git
Log | Files | Refs | Feed | LICENSE

build.c (10788B)


      1 /* See LICENSE for license details. */
      2 /* NOTE: inspired by nob: https://github.com/tsoding/nob.h */
      3 #define COMMON_FLAGS "-std=c11", "-pipe", "-Wall"
      4 
      5 #define OUTDIR    "out"
      6 #define OUTPUT(s) OUTDIR "/" s
      7 
      8 #include "compiler.h"
      9 #include "util.h"
     10 
     11 #include <stdarg.h>
     12 #include <stdio.h>
     13 
     14 #define is_aarch64 ARCH_ARM64
     15 #define is_amd64   ARCH_X64
     16 #define is_unix    OS_LINUX
     17 #define is_w32     OS_WINDOWS
     18 #define is_clang   COMPILER_CLANG
     19 
     20 #if OS_LINUX
     21 
     22   #include <errno.h>
     23   #include <string.h>
     24   #include <sys/select.h>
     25   #include <sys/wait.h>
     26 
     27   #include "os_linux.c"
     28 
     29   #define OS_MAIN "main_linux.c"
     30 
     31 #elif OS_WINDOWS
     32 
     33   #include "os_win32.c"
     34 
     35   #define OS_MAIN "main_w32.c"
     36 
     37 #else
     38   #error Unsupported Platform
     39 #endif
     40 
     41 #if COMPILER_CLANG
     42   #define COMPILER "clang"
     43 #elif COMPILER_MSVC
     44   #define COMPILER "cl"
     45 #else
     46   #define COMPILER "cc"
     47 #endif
     48 
     49 #define shift(list, count) ((count)--, *(list)++)
     50 
     51 #define da_append_count(a, s, items, item_count) do { \
     52 	da_reserve((a), (s), (s)->count + (item_count));                                \
     53 	mem_copy((s)->data + (s)->count, (items), sizeof(*(items)) * (uz)(item_count)); \
     54 	(s)->count += (item_count);                                                     \
     55 } while (0)
     56 
     57 #define cmd_append_count da_append_count
     58 #define cmd_append(a, s, ...) da_append_count(a, s, ((char *[]){__VA_ARGS__}), \
     59                                              (sz)(sizeof((char *[]){__VA_ARGS__}) / sizeof(char *)))
     60 
     61 typedef struct {
     62 	char **data;
     63 	sz     count;
     64 	sz     capacity;
     65 } CommandList;
     66 
     67 typedef struct {
     68 	b32   debug;
     69 	b32   generic;
     70 	b32   report;
     71 	b32   sanitize;
     72 
     73 	b32   encode_video;
     74 	c8   *video_name;
     75 } Options;
     76 
     77 #define die(fmt, ...) die_("%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
     78 function void __attribute__((noreturn))
     79 die_(char *format, ...)
     80 {
     81 	va_list ap;
     82 	va_start(ap, format);
     83 	/* TODO(rnp): proper log */
     84 	vfprintf(stderr, format, ap);
     85 	va_end(ap);
     86 	os_fatal(str8(""));
     87 }
     88 
     89 function b32
     90 str8_contains(str8 s, u8 byte)
     91 {
     92 	b32 result = 0;
     93 	for (sz i = 0 ; !result && i < s.len; i++)
     94 		result |= s.data[i] == byte;
     95 	return result;
     96 }
     97 
     98 function void
     99 stream_push_command(Stream *s, CommandList *c)
    100 {
    101 	if (!s->errors) {
    102 		for (sz i = 0; i < c->count; i++) {
    103 			str8 item = c_str_to_str8(c->data[i]);
    104 			if (item.len) {
    105 				b32 escape = str8_contains(item, ' ') || str8_contains(item, '"');
    106 				if (escape) stream_append_byte(s, '\'');
    107 				stream_append_str8(s, item);
    108 				if (escape) stream_append_byte(s, '\'');
    109 				if (i != c->count - 1) stream_append_byte(s, ' ');
    110 			}
    111 		}
    112 	}
    113 }
    114 
    115 #if OS_LINUX
    116 
    117 function b32
    118 os_rename_file(char *name, char *new)
    119 {
    120 	b32 result = rename(name, new) != -1;
    121 	return result;
    122 }
    123 
    124 function b32
    125 os_remove_file(char *name)
    126 {
    127 	b32 result = remove(name) != -1;
    128 	return result;
    129 }
    130 
    131 function u64
    132 os_get_filetime(char *file)
    133 {
    134 	struct stat sb;
    135 	u64 result = (u64)-1;
    136 	if (stat(file, &sb) != -1)
    137 		result = (u64)sb.st_mtim.tv_sec;
    138 	return result;
    139 }
    140 
    141 function sptr
    142 os_spawn_process(CommandList *cmd, Stream sb)
    143 {
    144 	pid_t result = fork();
    145 	switch (result) {
    146 	case -1: die("failed to fork command: %s: %s\n", cmd->data[0], strerror(errno)); break;
    147 	case  0: {
    148 		if (execvp(cmd->data[0], cmd->data) == -1)
    149 			die("failed to exec command: %s: %s\n", cmd->data[0], strerror(errno));
    150 		unreachable();
    151 	} break;
    152 	}
    153 	return (sptr)result;
    154 }
    155 
    156 function b32
    157 os_wait_close_process(sptr handle)
    158 {
    159 	b32 result = 0;
    160 	for (;;) {
    161 		s32 status, wait_pid = waitpid((s32)handle, &status, 0);
    162 		if (wait_pid == -1)
    163 			die("failed to wait on child process: %s\n", strerror(errno));
    164 		if (wait_pid == handle) {
    165 			if (WIFEXITED(status)) {
    166 				status = WEXITSTATUS(status);
    167 				/* TODO(rnp): logging */
    168 				result = status == 0;
    169 				break;
    170 			}
    171 			if (WIFSIGNALED(status)) {
    172 				/* TODO(rnp): logging */
    173 				result = 0;
    174 				break;
    175 			}
    176 		} else {
    177 			/* TODO(rnp): handle multiple children */
    178 			InvalidCodePath;
    179 		}
    180 	}
    181 	return result;
    182 }
    183 
    184 #elif OS_WINDOWS
    185 
    186 enum {
    187 	MOVEFILE_REPLACE_EXISTING = 0x01,
    188 };
    189 
    190 W32(b32) CreateProcessA(u8 *, u8 *, sptr, sptr, b32, u32, sptr, u8 *, sptr, sptr);
    191 W32(b32) DeleteFileA(c8 *);
    192 W32(b32) GetExitCodeProcess(sptr handle, u32 *);
    193 W32(b32) GetFileTime(sptr, sptr, sptr, sptr);
    194 W32(b32) MoveFileExA(c8 *, c8 *, u32);
    195 W32(u32) WaitForSingleObject(sptr, u32);
    196 
    197 function b32
    198 os_rename_file(char *name, char *new)
    199 {
    200 	b32 result = MoveFileExA(name, new, MOVEFILE_REPLACE_EXISTING) != 0;
    201 	return result;
    202 }
    203 
    204 function b32
    205 os_remove_file(char *name)
    206 {
    207 	b32 result = DeleteFileA(name);
    208 	return result;
    209 }
    210 
    211 function u64
    212 os_get_filetime(char *file)
    213 {
    214 	u64 result = (u64)-1;
    215 	sptr h = CreateFileA(file, 0, 0, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
    216 	if (h != INVALID_FILE) {
    217 		struct { u32 low, high; } w32_filetime;
    218 		GetFileTime(h, 0, 0, (sptr)&w32_filetime);
    219 		result = (u64)w32_filetime.high << 32ULL | w32_filetime.low;
    220 		CloseHandle(h);
    221 	}
    222 	return result;
    223 }
    224 
    225 function sptr
    226 os_spawn_process(CommandList *cmd, Stream sb)
    227 {
    228 	struct {
    229 		u32 cb;
    230 		u8 *reserved, *desktop, *title;
    231 		u32 x, y, x_size, y_size, x_count_chars, y_count_chars;
    232 		u32 fill_attr, flags;
    233 		u16 show_window, reserved_2;
    234 		u8 *reserved_3;
    235 		sptr std_input, std_output, std_error;
    236 	} w32_startup_info = {
    237 		.cb = sizeof(w32_startup_info),
    238 		.flags = 0x100,
    239 		.std_input  = GetStdHandle(STD_INPUT_HANDLE),
    240 		.std_output = GetStdHandle(STD_OUTPUT_HANDLE),
    241 		.std_error  = GetStdHandle(STD_ERROR_HANDLE),
    242 	};
    243 
    244 	struct {
    245 		sptr phandle, thandle;
    246 		u32  pid, tid;
    247 	} w32_process_info = {0};
    248 
    249 	/* TODO(rnp): warn if we need to clamp last string */
    250 	sb.widx = MIN(sb.widx, KB(32) - 1);
    251 	if (sb.widx < sb.cap) sb.data[sb.widx]     = 0;
    252 	else                  sb.data[sb.widx - 1] = 0;
    253 
    254 	sptr result = INVALID_FILE;
    255 	if (CreateProcessA(0, sb.data, 0, 0, 1, 0, 0, 0, (sptr)&w32_startup_info,
    256 	                   (sptr)&w32_process_info))
    257 	{
    258 		CloseHandle(w32_process_info.thandle);
    259 		result = w32_process_info.phandle;
    260 	}
    261 	return result;
    262 }
    263 
    264 function b32
    265 os_wait_close_process(sptr handle)
    266 {
    267 	b32 result = WaitForSingleObject(handle, -1) != 0xFFFFFFFFUL;
    268 	if (result) {
    269 		u32 status;
    270 		GetExitCodeProcess(handle, &status);
    271 		result = status == 0;
    272 	}
    273 	CloseHandle(handle);
    274 	return result;
    275 }
    276 
    277 #endif
    278 
    279 #define needs_rebuild(b, ...) needs_rebuild_(b, ((char *[]){__VA_ARGS__}), \
    280                                              (sizeof((char *[]){__VA_ARGS__}) / sizeof(char *)))
    281 function b32
    282 needs_rebuild_(char *binary, char *deps[], sz deps_count)
    283 {
    284 	u64 binary_filetime = os_get_filetime(binary);
    285 	b32 result = binary_filetime == (u64)-1;
    286 	for (sz i = 0; i < deps_count; i++) {
    287 		u64 filetime = os_get_filetime(deps[i]);
    288 		result |= (filetime == (u64)-1) | (filetime > binary_filetime);
    289 	}
    290 	return result;
    291 }
    292 
    293 function b32
    294 run_synchronous(Arena a, CommandList *command)
    295 {
    296 	Stream sb = arena_stream(a);
    297 	stream_push_command(&sb, command);
    298 	printf("%.*s\n", (s32)sb.widx, sb.data);
    299 	return os_wait_close_process(os_spawn_process(command, sb));
    300 }
    301 
    302 function void
    303 check_rebuild_self(Arena arena, s32 argc, char *argv[])
    304 {
    305 	char *binary = shift(argv, argc);
    306 	if (needs_rebuild(binary, __FILE__, "os_win32.c", "os_linux.c", "util.c", "util.h")) {
    307 		Stream name_buffer = arena_stream(arena);
    308 		stream_append_str8s(&name_buffer, c_str_to_str8(binary), str8(".old"));
    309 		char *old_name = (char *)arena_stream_commit_zero(&arena, &name_buffer).data;
    310 
    311 		if (!os_rename_file(binary, old_name))
    312 			die("failed to move: %s -> %s\n", binary, old_name);
    313 
    314 		CommandList c = {0};
    315 		cmd_append(&arena, &c, COMPILER, "-march=native", "-O3", COMMON_FLAGS);
    316 		if (is_w32 && is_clang) cmd_append(&arena, &c, "-fms-extensions");
    317 		cmd_append(&arena, &c, "-Wno-unused-function", __FILE__, "-o", binary, (void *)0);
    318 		if (!run_synchronous(arena, &c)) {
    319 			os_rename_file(old_name, binary);
    320 			die("failed to rebuild self\n");
    321 		}
    322 		os_remove_file(old_name);
    323 
    324 		c.count = 0;
    325 		cmd_append(&arena, &c, binary);
    326 		cmd_append_count(&arena, &c, argv, argc);
    327 		cmd_append(&arena, &c, (void *)0);
    328 		if (!run_synchronous(arena, &c))
    329 			os_exit(1);
    330 
    331 		os_exit(0);
    332 	}
    333 }
    334 
    335 function b32
    336 str8_equal(str8 a, str8 b)
    337 {
    338 	b32 result = a.len == b.len;
    339 	for (sz i = 0; result && i < a.len; i++)
    340 		result = a.data[i] == b.data[i];
    341 	return result;
    342 }
    343 
    344 function void
    345 usage(char *argv0)
    346 {
    347 	die("%s [--debug] [--report] [--sanitize] [--encode-video 'output']\n"
    348 	    "    --debug:         dynamically link and build with debug symbols\n"
    349 	    "    --generic:       compile for a generic target (x86-64-v3 or armv8 with NEON)\n"
    350 	    "    --report:        print compilation stats (clang only)\n"
    351 	    , argv0);
    352 }
    353 
    354 function Options
    355 parse_options(s32 argc, char *argv[])
    356 {
    357 	Options result = {0};
    358 
    359 	char *argv0 = shift(argv, argc);
    360 	while (argc > 0) {
    361 		char *arg = shift(argv, argc);
    362 		str8 str    = c_str_to_str8(arg);
    363 		if (str8_equal(str, str8("--debug"))) {
    364 			result.debug = 1;
    365 		} else if (str8_equal(str, str8("--generic"))) {
    366 			result.generic = 1;
    367 		} else if (str8_equal(str, str8("--sanitize"))) {
    368 			result.sanitize = 1;
    369 		} else {
    370 			usage(argv0);
    371 		}
    372 	}
    373 
    374 	return result;
    375 }
    376 
    377 function CommandList
    378 cmd_base(Arena *a, Options *o)
    379 {
    380 	CommandList result = {0};
    381 	cmd_append(a, &result, COMPILER);
    382 
    383 	/* TODO(rnp): support cross compiling with clang */
    384 	if (!o->generic)     cmd_append(a, &result, "-march=native");
    385 	else if (is_amd64)   cmd_append(a, &result, "-march=x86-64-v3");
    386 	else if (is_aarch64) cmd_append(a, &result, "-march=armv8");
    387 
    388 	cmd_append(a, &result, COMMON_FLAGS);
    389 
    390 	if (o->debug) cmd_append(a, &result, "-O0", "-D_DEBUG", "-Wno-unused-function");
    391 	else          cmd_append(a, &result, "-O3");
    392 
    393 	if (is_w32 && is_clang) cmd_append(a, &result, "-fms-extensions");
    394 
    395 	if (o->debug && is_unix) cmd_append(a, &result, "-gdwarf-4");
    396 
    397 	if (o->sanitize) cmd_append(a, &result, "-fsanitize=address,undefined");
    398 
    399 	return result;
    400 }
    401 
    402 /* NOTE(rnp): produce pdbs on w32 */
    403 function void
    404 cmd_pdb(Arena *a, CommandList *cmd)
    405 {
    406 	if (is_w32 && is_clang)
    407 		cmd_append(a, cmd, "-fuse-ld=lld", "-g", "-gcodeview", "-Wl,--pdb=");
    408 }
    409 
    410 /* NOTE(rnp): gcc requires these to appear at the end for no reason at all */
    411 function void
    412 cmd_append_ldflags(Arena *a, CommandList *cc, b32 shared)
    413 {
    414 	cmd_pdb(a, cc);
    415 	cmd_append(a, cc, "-lglslang", "-lvulkan");
    416 	/* TODO(rnp): this shouldn't be necessary */
    417 	cmd_append(a, cc, "-lglslang-default-resource-limits");
    418 	if (is_w32)  cmd_append(a, cc, "-lgdi32", "-lwinmm");
    419 }
    420 
    421 extern s32
    422 main(s32 argc, char *argv[])
    423 {
    424 	Arena arena = os_alloc_arena(MB(8));
    425 	check_rebuild_self(arena, argc, argv);
    426 
    427 	Options options = parse_options(argc, argv);
    428 
    429 	CommandList c = cmd_base(&arena, &options);
    430 	if (is_unix) cmd_append(&arena, &c, "-D_GLFW_X11");
    431 	cmd_append(&arena, &c, "-Iexternal/glfw/include");
    432 
    433 	cmd_append(&arena, &c, OS_MAIN, "external/rglfw.c", "-o", "camera_cnn");
    434 	cmd_append_ldflags(&arena, &c, options.debug);
    435 	cmd_append(&arena, &c, (void *)0);
    436 
    437 	return !run_synchronous(arena, &c);
    438 }