Hawaii Hybrid
Loading...
Searching...
No Matches
nob.h
Go to the documentation of this file.
1/* nob - v1.22.0 - Public Domain - https://github.com/tsoding/nob.h
2
3 This library is the next generation of the [NoBuild](https://github.com/tsoding/nobuild) idea.
4
5 # Quick Example
6
7 ```c
8 // nob.c
9 #define NOB_IMPLEMENTATION
10 #include "nob.h"
11
12 int main(int argc, char **argv)
13 {
14 NOB_GO_REBUILD_URSELF(argc, argv);
15 Nob_Cmd cmd = {0};
16 nob_cmd_append(&cmd, "cc", "-Wall", "-Wextra", "-o", "main", "main.c");
17 if (!nob_cmd_run_sync(cmd)) return 1;
18 return 0;
19 }
20 ```
21
22 ```console
23 $ cc -o nob nob.c
24 $ ./nob
25 ```
26
27 The `nob` automatically rebuilds itself if `nob.c` is modified thanks to
28 the `NOB_GO_REBUILD_URSELF` macro (don't forget to check out how it works below)
29
30 # The Zoo of `nob_cmd_run_*` Functions
31
32 `Nob_Cmd` is just a dynamic array of strings which represents a command and its arguments.
33 First you append the arguments
34
35 ```c
36 Nob_Cmd cmd = {0};
37 nob_cmd_append(&cmd, "cc", "-Wall", "-Wextra", "-o", "main", "main.c");
38 ```
39
40 Then you run it
41
42 ```c
43 if (!nob_cmd_run_sync(cmd)) return 1;
44 ```
45
46 `*_sync` at the end indicates that the function blocks until the command finishes executing
47 and returns `true` on success and `false` on failure. You can run the command asynchronously
48 but you have to explitictly wait for it afterwards:
49
50 ```c
51 Nob_Proc p = nob_cmd_run_async(cmd);
52 if (p == NOB_INVALID_PROC) return 1;
53 if (!nob_proc_wait(p)) return 1;
54 ```
55
56 One of the problems with running commands like that is that `Nob_Cmd` still contains the arguments
57 from the previously run command. If you want to reuse the same `Nob_Cmd` you have to not forget to reset
58 it
59
60 ```c
61 Nob_Cmd cmd = {0};
62
63 nob_cmd_append(&cmd, "cc", "-Wall", "-Wextra", "-o", "main", "main.c");
64 if (!nob_cmd_run_sync(cmd)) return 1;
65 cmd.count = 0;
66
67 nob_cmd_append(&cmd, "./main", "foo", "bar", "baz");
68 if (!nob_cmd_run_sync(cmd)) return 1;
69 cmd.count = 0;
70 ```
71
72 Which is a bit error prone. To make it a bit easier we have `nob_cmd_run_sync_and_reset()` which
73 accepts `Nob_Cmd` by reference and resets it for you:
74
75 ```c
76 Nob_Cmd cmd = {0};
77
78 nob_cmd_append(&cmd, "cc", "-Wall", "-Wextra", "-o", "main", "main.c");
79 if (!nob_cmd_run_sync_and_reset(&cmd)) return 1;
80
81 nob_cmd_append(&cmd, "./main", "foo", "bar", "baz");
82 if (!nob_cmd_run_sync_and_reset(&cmd)) return 1;
83 ```
84
85 There is of course also `nob_cmd_run_async_and_reset()` to maintain the pattern.
86
87 The stdin, stdout and stderr of any command can be redirected by using `Nob_Cmd_Redirect` structure
88 along with `nob_cmd_run_sync_redirect()` or `nob_cmd_run_async_redirect()`
89
90 ```c
91 // Opening all the necessary files
92 Nob_Fd fdin = nob_fd_open_for_read("input.txt");
93 if (fdin == NOB_INVALID_FD) return 1;
94 Nob_Fd fdout = nob_fd_open_for_write("output.txt");
95 if (fdout == NOB_INVALID_FD) return 1;
96 Nob_Fd fderr = nob_fd_open_for_write("error.txt");
97 if (fderr == NOB_INVALID_FD) return 1;
98
99 // Preparing the command
100 Nob_Cmd cmd = {0};
101 nob_cmd_append(&cmd, "./main", "foo", "bar", "baz");
102
103 // Running the command synchronously redirecting the standard streams
104 bool ok = nob_cmd_run_sync_redirect(cmd, (Nob_Cmd_Redirect) {
105 .fdin = fdin,
106 .fdout = fdout,
107 .fderr = fderr,
108 });
109 if (!ok) return 1;
110
111 // Closing all the files
112 nob_fd_close(fdin);
113 nob_fd_close(fdout);
114 nob_fd_close(fderr);
115
116 // Reseting the command
117 cmd.count = 0;
118 ```
119
120 And of course if you find closing the files and reseting the command annoying we have
121 `nob_cmd_run_sync_redirect_and_reset()` and `nob_cmd_run_async_redirect_and_reset()`
122 which do all of that for you automatically.
123
124 All the Zoo of `nob_cmd_run_*` functions follows the same pattern: sync/async,
125 redirect/no redirect, and_reset/no and_reset. They always come in that order.
126
127 # Stripping off `nob_` Prefixes
128
129 Since Pure C does not have any namespaces we prefix each name of the API with the `nob_` to avoid any
130 potential conflicts with any other names in your code. But sometimes it is very annoying and makes
131 the code noisy. If you know that none of the names from nob.h conflict with anything in your code
132 you can enable NOB_STRIP_PREFIX macro and just drop all the prefixes:
133
134 ```c
135 // nob.c
136 #define NOB_IMPLEMENTATION
137 #define NOB_STRIP_PREFIX
138 #include "nob.h"
139
140 int main(int argc, char **argv)
141 {
142 NOB_GO_REBUILD_URSELF(argc, argv);
143 Cmd cmd = {0};
144 cmd_append(&cmd, "cc", "-Wall", "-Wextra", "-o", "main", "main.c");
145 if (!cmd_run_sync(cmd)) return 1;
146 return 0;
147 }
148 ```
149
150 Not all the names have strippable prefixes. All the redefinable names like `NOB_GO_REBUILD_URSELF`
151 for instance will retain their prefix even if NOB_STRIP_PREFIX is enabled. Notable exception is the
152 nob_log() function. Stripping away the prefix results in log() which was historically always referring
153 to the natural logarithmic function that is already defined in math.h. So there is no reason to strip
154 off the prefix for nob_log(). Another exception is nob_rename() which collides with the widely known
155 POSIX function rename(2) if you strip the prefix off.
156
157 The prefixes are stripped off only on the level of preprocessor. The names of the functions in the
158 compiled object file will still retain the `nob_` prefix. Keep that in mind when you FFI with nob.h
159 from other languages (for whatever reason).
160
161 If only few specific names create conflicts for you, you can just #undef those names after the
162 `#include <nob.h>` since they are macros anyway.
163*/
164
165#ifndef NOB_H_
166#define NOB_H_
167#ifdef _WIN32
168#define _CRT_SECURE_NO_WARNINGS (1)
169#endif
170
171#ifndef NOBDEF
172/*
173 Goes before declarations and definitions of the nob functions. Useful to `#define NOBDEF static inline`
174 if your source code is a single file and you want the compiler to remove unused functions.
175*/
176#define NOBDEF
177#endif /* NOBDEF */
178
179#ifndef NOB_ASSERT
180#include <assert.h>
181#define NOB_ASSERT assert
182#endif /* NOB_ASSERT */
183
184#ifndef NOB_REALLOC
185#include <stdlib.h>
186#define NOB_REALLOC realloc
187#endif /* NOB_REALLOC */
188
189#ifndef NOB_FREE
190#include <stdlib.h>
191#define NOB_FREE free
192#endif /* NOB_FREE */
193
194#include <stdbool.h>
195#include <stdint.h>
196#include <stdlib.h>
197#include <stdio.h>
198#include <stdarg.h>
199#include <string.h>
200#include <errno.h>
201#include <ctype.h>
202#include <limits.h>
203
204#ifdef _WIN32
205# define WIN32_LEAN_AND_MEAN
206# define _WINUSER_
207# define _WINGDI_
208# define _IMM_
209# define _WINCON_
210# include <windows.h>
211# include <direct.h>
212# include <shellapi.h>
213#else
214# include <sys/types.h>
215# include <sys/wait.h>
216# include <sys/stat.h>
217# include <unistd.h>
218# include <fcntl.h>
219#endif
220
221#ifdef _WIN32
222# define NOB_LINE_END "\r\n"
223#else
224# define NOB_LINE_END "\n"
225#endif
226
227#if defined(__GNUC__) || defined(__clang__)
228// https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html
229# ifdef __MINGW_PRINTF_FORMAT
230# define NOB_PRINTF_FORMAT(STRING_INDEX, FIRST_TO_CHECK) __attribute__ ((format (__MINGW_PRINTF_FORMAT, STRING_INDEX, FIRST_TO_CHECK)))
231# else
232# define NOB_PRINTF_FORMAT(STRING_INDEX, FIRST_TO_CHECK) __attribute__ ((format (printf, STRING_INDEX, FIRST_TO_CHECK)))
233# endif // __MINGW_PRINTF_FORMAT
234#else
235// TODO: implement NOB_PRINTF_FORMAT for MSVC
236# define NOB_PRINTF_FORMAT(STRING_INDEX, FIRST_TO_CHECK)
237#endif
238
239#define NOB_UNUSED(value) (void)(value)
240#define NOB_TODO(message) do { fprintf(stderr, "%s:%d: TODO: %s\n", __FILE__, __LINE__, message); abort(); } while(0)
241#define NOB_UNREACHABLE(message) do { fprintf(stderr, "%s:%d: UNREACHABLE: %s\n", __FILE__, __LINE__, message); abort(); } while(0)
242
243#define NOB_ARRAY_LEN(array) (sizeof(array)/sizeof(array[0]))
244#define NOB_ARRAY_GET(array, index) \
245 (NOB_ASSERT((size_t)index < NOB_ARRAY_LEN(array)), array[(size_t)index])
246
253
254// Any messages with the level below nob_minimal_log_level are going to be suppressed.
256
257NOBDEF void nob_log(Nob_Log_Level level, const char *fmt, ...) NOB_PRINTF_FORMAT(2, 3);
258
259// It is an equivalent of shift command from bash. It basically pops an element from
260// the beginning of a sized array.
261#define nob_shift(xs, xs_sz) (NOB_ASSERT((xs_sz) > 0), (xs_sz)--, *(xs)++)
262// NOTE: nob_shift_args() is an alias for an old variant of nob_shift that only worked with
263// the command line arguments passed to the main() function. nob_shift() is more generic.
264// So nob_shift_args() is semi-deprecated, but I don't see much reason to urgently
265// remove it. This alias does not hurt anybody.
266#define nob_shift_args(argc, argv) nob_shift(*argv, *argc)
267
268typedef struct {
269 const char **items;
270 size_t count;
271 size_t capacity;
273
280
281NOBDEF bool nob_mkdir_if_not_exists(const char *path);
282NOBDEF bool nob_copy_file(const char *src_path, const char *dst_path);
283NOBDEF bool nob_copy_directory_recursively(const char *src_path, const char *dst_path);
284NOBDEF bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children);
285NOBDEF bool nob_write_entire_file(const char *path, const void *data, size_t size);
287NOBDEF bool nob_delete_file(const char *path);
288
289#define nob_return_defer(value) do { result = (value); goto defer; } while(0)
290
291// Initial capacity of a dynamic array
292#ifndef NOB_DA_INIT_CAP
293#define NOB_DA_INIT_CAP 256
294#endif
295
296#define nob_da_reserve(da, expected_capacity) \
297 do { \
298 if ((expected_capacity) > (da)->capacity) { \
299 if ((da)->capacity == 0) { \
300 (da)->capacity = NOB_DA_INIT_CAP; \
301 } \
302 while ((expected_capacity) > (da)->capacity) { \
303 (da)->capacity *= 2; \
304 } \
305 (da)->items = NOB_REALLOC((da)->items, (da)->capacity * sizeof(*(da)->items)); \
306 NOB_ASSERT((da)->items != NULL && "Buy more RAM lol"); \
307 } \
308 } while (0)
309
310// Append an item to a dynamic array
311#define nob_da_append(da, item) \
312 do { \
313 nob_da_reserve((da), (da)->count + 1); \
314 (da)->items[(da)->count++] = (item); \
315 } while (0)
316
317#define nob_da_free(da) NOB_FREE((da).items)
318
319// Append several items to a dynamic array
320#define nob_da_append_many(da, new_items, new_items_count) \
321 do { \
322 nob_da_reserve((da), (da)->count + (new_items_count)); \
323 memcpy((da)->items + (da)->count, (new_items), (new_items_count)*sizeof(*(da)->items)); \
324 (da)->count += (new_items_count); \
325 } while (0)
326
327#define nob_da_resize(da, new_size) \
328 do { \
329 nob_da_reserve((da), new_size); \
330 (da)->count = (new_size); \
331 } while (0)
332
333#define nob_da_last(da) (da)->items[(NOB_ASSERT((da)->count > 0), (da)->count-1)]
334#define nob_da_remove_unordered(da, i) \
335 do { \
336 size_t j = (i); \
337 NOB_ASSERT(j < (da)->count); \
338 (da)->items[j] = (da)->items[--(da)->count]; \
339 } while(0)
340
341// Foreach over Dynamic Arrays. Example:
342// ```c
343// typedef struct {
344// int *items;
345// size_t count;
346// size_t capacity;
347// } Numbers;
348//
349// Numbers xs = {0};
350//
351// nob_da_append(&xs, 69);
352// nob_da_append(&xs, 420);
353// nob_da_append(&xs, 1337);
354//
355// nob_da_foreach(int, x, &xs) {
356// // `x` here is a pointer to the current element. You can get its index by taking a difference
357// // between `x` and the start of the array which is `x.items`.
358// size_t index = x - xs.items;
359// nob_log(INFO, "%zu: %d", index, *x);
360// }
361// ```
362#define nob_da_foreach(Type, it, da) for (Type *it = (da)->items; it < (da)->items + (da)->count; ++it)
363
364typedef struct {
365 char *items;
366 size_t count;
367 size_t capacity;
369
371NOBDEF int nob_sb_appendf(Nob_String_Builder *sb, const char *fmt, ...) NOB_PRINTF_FORMAT(2, 3);
372
373// Append a sized buffer to a string builder
374#define nob_sb_append_buf(sb, buf, size) nob_da_append_many(sb, buf, size)
375
376// Append a NULL-terminated string to a string builder
377#define nob_sb_append_cstr(sb, cstr) \
378 do { \
379 const char *s = (cstr); \
380 size_t n = strlen(s); \
381 nob_da_append_many(sb, s, n); \
382 } while (0)
383
384// Append a single NULL character at the end of a string builder. So then you can
385// use it a NULL-terminated C string
386#define nob_sb_append_null(sb) nob_da_append_many(sb, "", 1)
387
388// Free the memory allocated by a string builder
389#define nob_sb_free(sb) NOB_FREE((sb).items)
390
391// Process handle
392#ifdef _WIN32
393typedef HANDLE Nob_Proc;
394#define NOB_INVALID_PROC INVALID_HANDLE_VALUE
395typedef HANDLE Nob_Fd;
396#define NOB_INVALID_FD INVALID_HANDLE_VALUE
397#else
398typedef int Nob_Proc;
399#define NOB_INVALID_PROC (-1)
400typedef int Nob_Fd;
401#define NOB_INVALID_FD (-1)
402#endif // _WIN32
403
407
408typedef struct {
410 size_t count;
411 size_t capacity;
412} Nob_Procs;
413
414// Wait until the process has finished
416// Wait until all the processes have finished
418// Wait until all the processes have finished and empty the procs array
420// Append a new process to procs array and if procs.count reaches max_procs_count call nob_procs_wait_and_reset() on it
421NOBDEF bool nob_procs_append_with_flush(Nob_Procs *procs, Nob_Proc proc, size_t max_procs_count);
422
423// A command - the main workhorse of Nob. Nob is all about building commands and running them
424typedef struct {
425 const char **items;
426 size_t count;
427 size_t capacity;
428} Nob_Cmd;
429
430// Example:
431// ```c
432// Nob_Fd fdin = nob_fd_open_for_read("input.txt");
433// if (fdin == NOB_INVALID_FD) fail();
434// Nob_Fd fdout = nob_fd_open_for_write("output.txt");
435// if (fdout == NOB_INVALID_FD) fail();
436// Nob_Cmd cmd = {0};
437// nob_cmd_append(&cmd, "cat");
438// if (!nob_cmd_run_sync_redirect_and_reset(&cmd, (Nob_Cmd_Redirect) {
439// .fdin = &fdin,
440// .fdout = &fdout
441// })) fail();
442// ```
448
449// Render a string representation of a command into a string builder. Keep in mind the the
450// string builder is not NULL-terminated by default. Use nob_sb_append_null if you plan to
451// use it as a C string.
453
454// TODO: implement C++ support for nob.h
455#define nob_cmd_append(cmd, ...) \
456 nob_da_append_many(cmd, \
457 ((const char*[]){__VA_ARGS__}), \
458 (sizeof((const char*[]){__VA_ARGS__})/sizeof(const char*)))
459
460#define nob_cmd_extend(cmd, other_cmd) \
461 nob_da_append_many(cmd, (other_cmd)->items, (other_cmd)->count)
462
463// Free all the memory allocated by command arguments
464#define nob_cmd_free(cmd) NOB_FREE(cmd.items)
465
466// Run command asynchronously
467#define nob_cmd_run_async(cmd) nob_cmd_run_async_redirect(cmd, (Nob_Cmd_Redirect) {0})
468// NOTE: nob_cmd_run_async_and_reset() is just like nob_cmd_run_async() except it also resets cmd.count to 0
469// so the Nob_Cmd instance can be seamlessly used several times in a row
471// Run redirected command asynchronously
473// Run redirected command asynchronously and set cmd.count to 0 and close all the opened files
475
476// Run command synchronously
478// NOTE: nob_cmd_run_sync_and_reset() is just like nob_cmd_run_sync() except it also resets cmd.count to 0
479// so the Nob_Cmd instance can be seamlessly used several times in a row
481// Run redirected command synchronously
483// Run redirected command synchronously and set cmd.count to 0 and close all the opened files
485
486#ifndef NOB_TEMP_CAPACITY
487#define NOB_TEMP_CAPACITY (8*1024*1024)
488#endif // NOB_TEMP_CAPACITY
489NOBDEF char *nob_temp_strdup(const char *cstr);
490NOBDEF void *nob_temp_alloc(size_t size);
491NOBDEF char *nob_temp_sprintf(const char *format, ...) NOB_PRINTF_FORMAT(1, 2);
492// nob_temp_reset() - Resets the entire temporary storage to 0.
493//
494// It is generally not recommended to call this function ever. What you usually want to do is let's say you have a loop,
495// that allocates some temporary objects and cleans them up at the end of each iteration. You should use
496// nob_temp_save() and nob_temp_rewind() to organize such loop like this:
497//
498// ```c
499// char *message = nob_temp_sprintf("This message is still valid after the loop below");
500// while (!quit) {
501// size_t mark = nob_temp_save();
502// nob_temp_alloc(69);
503// nob_temp_alloc(420);
504// nob_temp_alloc(1337);
505// nob_temp_rewind(mark);
506// }
507// printf("%s\n", message);
508// ```
509//
510// That way all the temporary allocations created before the loop are still valid even after the loop.
511// Such save/rewind blocks define lifetime boundaries of the temporary objects which also could be nested.
512// This turns the temporary storage into kind of a second stack with a more manual management.
515NOBDEF void nob_temp_rewind(size_t checkpoint);
516
517// Given any path returns the last part of that path.
518// "/path/to/a/file.c" -> "file.c"; "/path/to/a/directory" -> "directory"
519NOBDEF const char *nob_path_name(const char *path);
520NOBDEF bool nob_rename(const char *old_path, const char *new_path);
521NOBDEF int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count);
522NOBDEF int nob_needs_rebuild1(const char *output_path, const char *input_path);
523NOBDEF int nob_file_exists(const char *file_path);
525NOBDEF bool nob_set_current_dir(const char *path);
526
527// TODO: we should probably document somewhere all the compiler we support
528
529// The nob_cc_* macros try to abstract away the specific compiler.
530// They are verify basic and not particularly flexible, but you can redefine them if you need to
531// or not use them at all and create your own abstraction on top of Nob_Cmd.
532
533#ifndef nob_cc
534# if _WIN32
535# if defined(__GNUC__)
536# define nob_cc(cmd) nob_cmd_append(cmd, "cc")
537# elif defined(__clang__)
538# define nob_cc(cmd) nob_cmd_append(cmd, "clang")
539# elif defined(_MSC_VER)
540# define nob_cc(cmd) nob_cmd_append(cmd, "cl.exe")
541# endif
542# else
543# define nob_cc(cmd) nob_cmd_append(cmd, "cc")
544# endif
545#endif // nob_cc
546
547#ifndef nob_cc_flags
548# if defined(_MSC_VER) && !defined(__clang__)
549# define nob_cc_flags(cmd) nob_cmd_append(cmd, "/W4", "/nologo", "/D_CRT_SECURE_NO_WARNINGS")
550# else
551# define nob_cc_flags(cmd) nob_cmd_append(cmd, "-Wall", "-Wextra")
552# endif
553#endif // nob_cc_output
554
555#ifndef nob_cc_output
556# if defined(_MSC_VER) && !defined(__clang__)
557# define nob_cc_output(cmd, output_path) nob_cmd_append(cmd, nob_temp_sprintf("/Fe:%s", (output_path)))
558# else
559# define nob_cc_output(cmd, output_path) nob_cmd_append(cmd, "-o", (output_path))
560# endif
561#endif // nob_cc_output
562
563#ifndef nob_cc_inputs
564# define nob_cc_inputs(cmd, ...) nob_cmd_append(cmd, __VA_ARGS__)
565#endif // nob_cc_inputs
566
567// TODO: add MinGW support for Go Rebuild Urself™ Technology and all the nob_cc_* macros above
568// Musializer contributors came up with a pretty interesting idea of an optional prefix macro which could be useful for
569// MinGW support:
570// https://github.com/tsoding/musializer/blob/b7578cc76b9ecb573d239acc9ccf5a04d3aba2c9/src_build/nob_win64_mingw.c#L3-L9
571// TODO: Maybe instead NOB_REBUILD_URSELF macro, the Go Rebuild Urself™ Technology should use the
572// user defined nob_cc_* macros instead?
573#ifndef NOB_REBUILD_URSELF
574# if defined(_WIN32)
575# if defined(__GNUC__)
576# define NOB_REBUILD_URSELF(binary_path, source_path) "gcc", "-o", binary_path, source_path
577# elif defined(__clang__)
578# define NOB_REBUILD_URSELF(binary_path, source_path) "clang", "-o", binary_path, source_path
579# elif defined(_MSC_VER)
580# define NOB_REBUILD_URSELF(binary_path, source_path) "cl.exe", nob_temp_sprintf("/Fe:%s", (binary_path)), source_path
581# endif
582# else
583# define NOB_REBUILD_URSELF(binary_path, source_path) "cc", "-o", binary_path, source_path
584# endif
585#endif
586
587// Go Rebuild Urself™ Technology
588//
589// How to use it:
590// int main(int argc, char** argv) {
591// NOB_GO_REBUILD_URSELF(argc, argv);
592// // actual work
593// return 0;
594// }
595//
596// After you added this macro every time you run ./nob it will detect
597// that you modified its original source code and will try to rebuild itself
598// before doing any actual work. So you only need to bootstrap your build system
599// once.
600//
601// The modification is detected by comparing the last modified times of the executable
602// and its source code. The same way the make utility usually does it.
603//
604// The rebuilding is done by using the NOB_REBUILD_URSELF macro which you can redefine
605// if you need a special way of bootstraping your build system. (which I personally
606// do not recommend since the whole idea of NoBuild is to keep the process of bootstrapping
607// as simple as possible and doing all of the actual work inside of ./nob)
608//
609NOBDEF void nob__go_rebuild_urself(int argc, char **argv, const char *source_path, ...);
610#define NOB_GO_REBUILD_URSELF(argc, argv) nob__go_rebuild_urself(argc, argv, __FILE__, NULL)
611// Sometimes your nob.c includes additional files, so you want the Go Rebuild Urself™ Technology to check
612// if they also were modified and rebuild nob.c accordingly. For that we have NOB_GO_REBUILD_URSELF_PLUS():
613// ```c
614// #define NOB_IMPLEMENTATION
615// #include "nob.h"
616//
617// #include "foo.c"
618// #include "bar.c"
619//
620// int main(int argc, char **argv)
621// {
622// NOB_GO_REBUILD_URSELF_PLUS(argc, argv, "foo.c", "bar.c");
623// // ...
624// return 0;
625// }
626#define NOB_GO_REBUILD_URSELF_PLUS(argc, argv, ...) nob__go_rebuild_urself(argc, argv, __FILE__, __VA_ARGS__, NULL);
627
628typedef struct {
629 size_t count;
630 const char *data;
632
634
641NOBDEF bool nob_sv_end_with(Nob_String_View sv, const char *cstr);
644NOBDEF Nob_String_View nob_sv_from_parts(const char *data, size_t count);
645// nob_sb_to_sv() enables you to just view Nob_String_Builder as Nob_String_View
646#define nob_sb_to_sv(sb) nob_sv_from_parts((sb).items, (sb).count)
647
648// printf macros for String_View
649#ifndef SV_Fmt
650#define SV_Fmt "%.*s"
651#endif // SV_Fmt
652#ifndef SV_Arg
653#define SV_Arg(sv) (int) (sv).count, (sv).data
654#endif // SV_Arg
655// USAGE:
656// String_View name = ...;
657// printf("Name: "SV_Fmt"\n", SV_Arg(name));
658
659// DEPRECATED: Usage of the bundled minirent.h below is deprecated, because it introduces more
660// problems than it solves. It will be removed in the next major release of nob.h. In the meantime,
661// it is recommended to `#define NOB_NO_MINIRENT` if it causes problems for you.
662
663// minirent.h HEADER BEGIN ////////////////////////////////////////
664// Copyright 2021 Alexey Kutepov <reximkut@gmail.com>
665//
666// Permission is hereby granted, free of charge, to any person obtaining
667// a copy of this software and associated documentation files (the
668// "Software"), to deal in the Software without restriction, including
669// without limitation the rights to use, copy, modify, merge, publish,
670// distribute, sublicense, and/or sell copies of the Software, and to
671// permit persons to whom the Software is furnished to do so, subject to
672// the following conditions:
673//
674// The above copyright notice and this permission notice shall be
675// included in all copies or substantial portions of the Software.
676//
677// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
678// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
679// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
680// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
681// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
682// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
683// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
684//
685// ============================================================
686//
687// minirent — 0.0.1 — A subset of dirent interface for Windows.
688//
689// https://github.com/tsoding/minirent
690//
691// ============================================================
692//
693// ChangeLog (https://semver.org/ is implied)
694//
695// 0.0.2 Automatically include dirent.h on non-Windows
696// platforms
697// 0.0.1 First Official Release
698
699#if !defined(_WIN32) || defined(NOB_NO_MINIRENT)
700#include <dirent.h>
701#else // _WIN32
702
703#define WIN32_LEAN_AND_MEAN
704#include "windows.h"
705
706struct dirent
707{
708 char d_name[MAX_PATH+1];
709};
710
711typedef struct DIR DIR;
712
713static DIR *opendir(const char *dirpath);
714static struct dirent *readdir(DIR *dirp);
715static int closedir(DIR *dirp);
716
717#endif // _WIN32
718// minirent.h HEADER END ////////////////////////////////////////
719
720#ifdef _WIN32
721
722NOBDEF char *nob_win32_error_message(DWORD err);
723
724#endif // _WIN32
725
726#endif // NOB_H_
727
728#ifdef NOB_IMPLEMENTATION
729
730// Any messages with the level below nob_minimal_log_level are going to be suppressed.
732
733#ifdef _WIN32
734
735// Base on https://stackoverflow.com/a/75644008
736// > .NET Core uses 4096 * sizeof(WCHAR) buffer on stack for FormatMessageW call. And...thats it.
737// >
738// > https://github.com/dotnet/runtime/blob/3b63eb1346f1ddbc921374a5108d025662fb5ffd/src/coreclr/utilcode/posterror.cpp#L264-L265
739#ifndef NOB_WIN32_ERR_MSG_SIZE
740#define NOB_WIN32_ERR_MSG_SIZE (4 * 1024)
741#endif // NOB_WIN32_ERR_MSG_SIZE
742
743NOBDEF char *nob_win32_error_message(DWORD err) {
744 static char win32ErrMsg[NOB_WIN32_ERR_MSG_SIZE] = {0};
745 DWORD errMsgSize = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, LANG_USER_DEFAULT, win32ErrMsg,
746 NOB_WIN32_ERR_MSG_SIZE, NULL);
747
748 if (errMsgSize == 0) {
749 if (GetLastError() != ERROR_MR_MID_NOT_FOUND) {
750 if (sprintf(win32ErrMsg, "Could not get error message for 0x%lX", err) > 0) {
751 return (char *)&win32ErrMsg;
752 } else {
753 return NULL;
754 }
755 } else {
756 if (sprintf(win32ErrMsg, "Invalid Windows Error code (0x%lX)", err) > 0) {
757 return (char *)&win32ErrMsg;
758 } else {
759 return NULL;
760 }
761 }
762 }
763
764 while (errMsgSize > 1 && isspace(win32ErrMsg[errMsgSize - 1])) {
765 win32ErrMsg[--errMsgSize] = '\0';
766 }
767
768 return win32ErrMsg;
769}
770
771#endif // _WIN32
772
773// The implementation idea is stolen from https://github.com/zhiayang/nabs
774NOBDEF void nob__go_rebuild_urself(int argc, char **argv, const char *source_path, ...)
775{
776 const char *binary_path = nob_shift(argv, argc);
777#ifdef _WIN32
778 // On Windows executables almost always invoked without extension, so
779 // it's ./nob, not ./nob.exe. For renaming the extension is a must.
780 if (!nob_sv_end_with(nob_sv_from_cstr(binary_path), ".exe")) {
781 binary_path = nob_temp_sprintf("%s.exe", binary_path);
782 }
783#endif
784
785 Nob_File_Paths source_paths = {0};
786 nob_da_append(&source_paths, source_path);
787 va_list args;
788 va_start(args, source_path);
789 for (;;) {
790 const char *path = va_arg(args, const char*);
791 if (path == NULL) break;
792 nob_da_append(&source_paths, path);
793 }
794 va_end(args);
795
796 int rebuild_is_needed = nob_needs_rebuild(binary_path, source_paths.items, source_paths.count);
797 if (rebuild_is_needed < 0) exit(1); // error
798 if (!rebuild_is_needed) { // no rebuild is needed
799 NOB_FREE(source_paths.items);
800 return;
801 }
802
803 Nob_Cmd cmd = {0};
804
805 const char *old_binary_path = nob_temp_sprintf("%s.old", binary_path);
806
807 if (!nob_rename(binary_path, old_binary_path)) exit(1);
808 nob_cmd_append(&cmd, NOB_REBUILD_URSELF(binary_path, source_path));
809 if (!nob_cmd_run_sync_and_reset(&cmd)) {
810 nob_rename(old_binary_path, binary_path);
811 exit(1);
812 }
813#ifdef NOB_EXPERIMENTAL_DELETE_OLD
814 // TODO: this is an experimental behavior behind a compilation flag.
815 // Once it is confirmed that it does not cause much problems on both POSIX and Windows
816 // we may turn it on by default.
817 nob_delete_file(old_binary_path);
818#endif // NOB_EXPERIMENTAL_DELETE_OLD
819
820 nob_cmd_append(&cmd, binary_path);
821 nob_da_append_many(&cmd, argv, argc);
822 if (!nob_cmd_run_sync_and_reset(&cmd)) exit(1);
823 exit(0);
824}
825
826static size_t nob_temp_size = 0;
827static char nob_temp[NOB_TEMP_CAPACITY] = {0};
828
829NOBDEF bool nob_mkdir_if_not_exists(const char *path)
830{
831#ifdef _WIN32
832 int result = _mkdir(path);
833#else
834 int result = mkdir(path, 0755);
835#endif
836 if (result < 0) {
837 if (errno == EEXIST) {
838 nob_log(NOB_INFO, "directory `%s` already exists", path);
839 return true;
840 }
841 nob_log(NOB_ERROR, "could not create directory `%s`: %s", path, strerror(errno));
842 return false;
843 }
844
845 nob_log(NOB_INFO, "created directory `%s`", path);
846 return true;
847}
848
849NOBDEF bool nob_copy_file(const char *src_path, const char *dst_path)
850{
851 nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path);
852#ifdef _WIN32
853 if (!CopyFile(src_path, dst_path, FALSE)) {
854 nob_log(NOB_ERROR, "Could not copy file: %s", nob_win32_error_message(GetLastError()));
855 return false;
856 }
857 return true;
858#else
859 int src_fd = -1;
860 int dst_fd = -1;
861 size_t buf_size = 32*1024;
862 char *buf = NOB_REALLOC(NULL, buf_size);
863 NOB_ASSERT(buf != NULL && "Buy more RAM lol!!");
864 bool result = true;
865
866 src_fd = open(src_path, O_RDONLY);
867 if (src_fd < 0) {
868 nob_log(NOB_ERROR, "Could not open file %s: %s", src_path, strerror(errno));
869 nob_return_defer(false);
870 }
871
872 struct stat src_stat;
873 if (fstat(src_fd, &src_stat) < 0) {
874 nob_log(NOB_ERROR, "Could not get mode of file %s: %s", src_path, strerror(errno));
875 nob_return_defer(false);
876 }
877
878 dst_fd = open(dst_path, O_CREAT | O_TRUNC | O_WRONLY, src_stat.st_mode);
879 if (dst_fd < 0) {
880 nob_log(NOB_ERROR, "Could not create file %s: %s", dst_path, strerror(errno));
881 nob_return_defer(false);
882 }
883
884 for (;;) {
885 ssize_t n = read(src_fd, buf, buf_size);
886 if (n == 0) break;
887 if (n < 0) {
888 nob_log(NOB_ERROR, "Could not read from file %s: %s", src_path, strerror(errno));
889 nob_return_defer(false);
890 }
891 char *buf2 = buf;
892 while (n > 0) {
893 ssize_t m = write(dst_fd, buf2, n);
894 if (m < 0) {
895 nob_log(NOB_ERROR, "Could not write to file %s: %s", dst_path, strerror(errno));
896 nob_return_defer(false);
897 }
898 n -= m;
899 buf2 += m;
900 }
901 }
902
903defer:
904 NOB_FREE(buf);
905 close(src_fd);
906 close(dst_fd);
907 return result;
908#endif
909}
910
912{
913 for (size_t i = 0; i < cmd.count; ++i) {
914 const char *arg = cmd.items[i];
915 if (arg == NULL) break;
916 if (i > 0) nob_sb_append_cstr(render, " ");
917 if (!strchr(arg, ' ')) {
918 nob_sb_append_cstr(render, arg);
919 } else {
920 nob_da_append(render, '\'');
921 nob_sb_append_cstr(render, arg);
922 nob_da_append(render, '\'');
923 }
924 }
925}
926
927#ifdef _WIN32
928// https://learn.microsoft.com/en-gb/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way
929static void nob__win32_cmd_quote(Nob_Cmd cmd, Nob_String_Builder *quoted)
930{
931 for (size_t i = 0; i < cmd.count; ++i) {
932 const char *arg = cmd.items[i];
933 if (arg == NULL) break;
934 size_t len = strlen(arg);
935 if (i > 0) nob_da_append(quoted, ' ');
936 if (len != 0 && NULL == strpbrk(arg, " \t\n\v\"")) {
937 // no need to quote
938 nob_da_append_many(quoted, arg, len);
939 } else {
940 // we need to escape:
941 // 1. double quotes in the original arg
942 // 2. consequent backslashes before a double quote
943 size_t backslashes = 0;
944 nob_da_append(quoted, '\"');
945 for (size_t j = 0; j < len; ++j) {
946 char x = arg[j];
947 if (x == '\\') {
948 backslashes += 1;
949 } else {
950 if (x == '\"') {
951 // escape backslashes (if any) and the double quote
952 for (size_t k = 0; k < 1+backslashes; ++k) {
953 nob_da_append(quoted, '\\');
954 }
955 }
956 backslashes = 0;
957 }
958 nob_da_append(quoted, x);
959 }
960 // escape backslashes (if any)
961 for (size_t k = 0; k < backslashes; ++k) {
962 nob_da_append(quoted, '\\');
963 }
964 nob_da_append(quoted, '\"');
965 }
966 }
967}
968#endif
969
971{
972 if (cmd.count < 1) {
973 nob_log(NOB_ERROR, "Could not run empty command");
974 return NOB_INVALID_PROC;
975 }
976
977 Nob_String_Builder sb = {0};
978 nob_cmd_render(cmd, &sb);
980 nob_log(NOB_INFO, "CMD: %s", sb.items);
981 nob_sb_free(sb);
982 memset(&sb, 0, sizeof(sb));
983
984#ifdef _WIN32
985 // https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output
986
987 STARTUPINFO siStartInfo;
988 ZeroMemory(&siStartInfo, sizeof(siStartInfo));
989 siStartInfo.cb = sizeof(STARTUPINFO);
990 // NOTE: theoretically setting NULL to std handles should not be a problem
991 // https://docs.microsoft.com/en-us/windows/console/getstdhandle?redirectedfrom=MSDN#attachdetach-behavior
992 // TODO: check for errors in GetStdHandle
993 siStartInfo.hStdError = redirect.fderr ? *redirect.fderr : GetStdHandle(STD_ERROR_HANDLE);
994 siStartInfo.hStdOutput = redirect.fdout ? *redirect.fdout : GetStdHandle(STD_OUTPUT_HANDLE);
995 siStartInfo.hStdInput = redirect.fdin ? *redirect.fdin : GetStdHandle(STD_INPUT_HANDLE);
996 siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
997
998 PROCESS_INFORMATION piProcInfo;
999 ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
1000
1001 nob__win32_cmd_quote(cmd, &sb);
1002 nob_sb_append_null(&sb);
1003 BOOL bSuccess = CreateProcessA(NULL, sb.items, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
1004 nob_sb_free(sb);
1005
1006 if (!bSuccess) {
1007 nob_log(NOB_ERROR, "Could not create child process for %s: %s", cmd.items[0], nob_win32_error_message(GetLastError()));
1008 return NOB_INVALID_PROC;
1009 }
1010
1011 CloseHandle(piProcInfo.hThread);
1012
1013 return piProcInfo.hProcess;
1014#else
1015 pid_t cpid = fork();
1016 if (cpid < 0) {
1017 nob_log(NOB_ERROR, "Could not fork child process: %s", strerror(errno));
1018 return NOB_INVALID_PROC;
1019 }
1020
1021 if (cpid == 0) {
1022 if (redirect.fdin) {
1023 if (dup2(*redirect.fdin, STDIN_FILENO) < 0) {
1024 nob_log(NOB_ERROR, "Could not setup stdin for child process: %s", strerror(errno));
1025 exit(1);
1026 }
1027 }
1028
1029 if (redirect.fdout) {
1030 if (dup2(*redirect.fdout, STDOUT_FILENO) < 0) {
1031 nob_log(NOB_ERROR, "Could not setup stdout for child process: %s", strerror(errno));
1032 exit(1);
1033 }
1034 }
1035
1036 if (redirect.fderr) {
1037 if (dup2(*redirect.fderr, STDERR_FILENO) < 0) {
1038 nob_log(NOB_ERROR, "Could not setup stderr for child process: %s", strerror(errno));
1039 exit(1);
1040 }
1041 }
1042
1043 // NOTE: This leaks a bit of memory in the child process.
1044 // But do we actually care? It's a one off leak anyway...
1045 Nob_Cmd cmd_null = {0};
1046 nob_da_append_many(&cmd_null, cmd.items, cmd.count);
1047 nob_cmd_append(&cmd_null, NULL);
1048
1049 if (execvp(cmd.items[0], (char * const*) cmd_null.items) < 0) {
1050 nob_log(NOB_ERROR, "Could not exec child process for %s: %s", cmd.items[0], strerror(errno));
1051 exit(1);
1052 }
1053 NOB_UNREACHABLE("nob_cmd_run_async_redirect");
1054 }
1055
1056 return cpid;
1057#endif
1058}
1059
1061{
1062 Nob_Proc proc = nob_cmd_run_async(*cmd);
1063 cmd->count = 0;
1064 return proc;
1065}
1066
1068{
1069 Nob_Proc proc = nob_cmd_run_async_redirect(*cmd, redirect);
1070 cmd->count = 0;
1071 if (redirect.fdin) {
1072 nob_fd_close(*redirect.fdin);
1073 *redirect.fdin = NOB_INVALID_FD;
1074 }
1075 if (redirect.fdout) {
1076 nob_fd_close(*redirect.fdout);
1077 *redirect.fdout = NOB_INVALID_FD;
1078 }
1079 if (redirect.fderr) {
1080 nob_fd_close(*redirect.fderr);
1081 *redirect.fderr = NOB_INVALID_FD;
1082 }
1083 return proc;
1084}
1085
1086NOBDEF Nob_Fd nob_fd_open_for_read(const char *path)
1087{
1088#ifndef _WIN32
1089 Nob_Fd result = open(path, O_RDONLY);
1090 if (result < 0) {
1091 nob_log(NOB_ERROR, "Could not open file %s: %s", path, strerror(errno));
1092 return NOB_INVALID_FD;
1093 }
1094 return result;
1095#else
1096 // https://docs.microsoft.com/en-us/windows/win32/fileio/opening-a-file-for-reading-or-writing
1097 SECURITY_ATTRIBUTES saAttr = {0};
1098 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
1099 saAttr.bInheritHandle = TRUE;
1100
1101 Nob_Fd result = CreateFile(
1102 path,
1103 GENERIC_READ,
1104 0,
1105 &saAttr,
1106 OPEN_EXISTING,
1107 FILE_ATTRIBUTE_READONLY,
1108 NULL);
1109
1110 if (result == INVALID_HANDLE_VALUE) {
1111 nob_log(NOB_ERROR, "Could not open file %s: %s", path, nob_win32_error_message(GetLastError()));
1112 return NOB_INVALID_FD;
1113 }
1114
1115 return result;
1116#endif // _WIN32
1117}
1118
1119NOBDEF Nob_Fd nob_fd_open_for_write(const char *path)
1120{
1121#ifndef _WIN32
1122 Nob_Fd result = open(path,
1123 O_WRONLY | O_CREAT | O_TRUNC,
1124 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1125 if (result < 0) {
1126 nob_log(NOB_ERROR, "could not open file %s: %s", path, strerror(errno));
1127 return NOB_INVALID_FD;
1128 }
1129 return result;
1130#else
1131 SECURITY_ATTRIBUTES saAttr = {0};
1132 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
1133 saAttr.bInheritHandle = TRUE;
1134
1135 Nob_Fd result = CreateFile(
1136 path, // name of the write
1137 GENERIC_WRITE, // open for writing
1138 0, // do not share
1139 &saAttr, // default security
1140 CREATE_ALWAYS, // create always
1141 FILE_ATTRIBUTE_NORMAL, // normal file
1142 NULL // no attr. template
1143 );
1144
1145 if (result == INVALID_HANDLE_VALUE) {
1146 nob_log(NOB_ERROR, "Could not open file %s: %s", path, nob_win32_error_message(GetLastError()));
1147 return NOB_INVALID_FD;
1148 }
1149
1150 return result;
1151#endif // _WIN32
1152}
1153
1154NOBDEF void nob_fd_close(Nob_Fd fd)
1155{
1156#ifdef _WIN32
1157 CloseHandle(fd);
1158#else
1159 close(fd);
1160#endif // _WIN32
1161}
1162
1164{
1165 bool success = true;
1166 for (size_t i = 0; i < procs.count; ++i) {
1167 success = nob_proc_wait(procs.items[i]) && success;
1168 }
1169 return success;
1170}
1171
1173{
1174 bool success = nob_procs_wait(*procs);
1175 procs->count = 0;
1176 return success;
1177}
1178
1179NOBDEF bool nob_proc_wait(Nob_Proc proc)
1180{
1181 if (proc == NOB_INVALID_PROC) return false;
1182
1183#ifdef _WIN32
1184 DWORD result = WaitForSingleObject(
1185 proc, // HANDLE hHandle,
1186 INFINITE // DWORD dwMilliseconds
1187 );
1188
1189 if (result == WAIT_FAILED) {
1190 nob_log(NOB_ERROR, "could not wait on child process: %s", nob_win32_error_message(GetLastError()));
1191 return false;
1192 }
1193
1194 DWORD exit_status;
1195 if (!GetExitCodeProcess(proc, &exit_status)) {
1196 nob_log(NOB_ERROR, "could not get process exit code: %s", nob_win32_error_message(GetLastError()));
1197 return false;
1198 }
1199
1200 if (exit_status != 0) {
1201 nob_log(NOB_ERROR, "command exited with exit code %lu", exit_status);
1202 return false;
1203 }
1204
1205 CloseHandle(proc);
1206
1207 return true;
1208#else
1209 for (;;) {
1210 int wstatus = 0;
1211 if (waitpid(proc, &wstatus, 0) < 0) {
1212 nob_log(NOB_ERROR, "could not wait on command (pid %d): %s", proc, strerror(errno));
1213 return false;
1214 }
1215
1216 if (WIFEXITED(wstatus)) {
1217 int exit_status = WEXITSTATUS(wstatus);
1218 if (exit_status != 0) {
1219 nob_log(NOB_ERROR, "command exited with exit code %d", exit_status);
1220 return false;
1221 }
1222
1223 break;
1224 }
1225
1226 if (WIFSIGNALED(wstatus)) {
1227 nob_log(NOB_ERROR, "command process was terminated by signal %d", WTERMSIG(wstatus));
1228 return false;
1229 }
1230 }
1231
1232 return true;
1233#endif
1234}
1235
1236NOBDEF bool nob_procs_append_with_flush(Nob_Procs *procs, Nob_Proc proc, size_t max_procs_count)
1237{
1238 nob_da_append(procs, proc);
1239
1240 if (procs->count >= max_procs_count) {
1241 if (!nob_procs_wait_and_reset(procs)) return false;
1242 }
1243
1244 return true;
1245}
1246
1248{
1249 Nob_Proc p = nob_cmd_run_async_redirect(cmd, redirect);
1250 if (p == NOB_INVALID_PROC) return false;
1251 return nob_proc_wait(p);
1252}
1253
1255{
1256 Nob_Proc p = nob_cmd_run_async(cmd);
1257 if (p == NOB_INVALID_PROC) return false;
1258 return nob_proc_wait(p);
1259}
1260
1262{
1263 bool p = nob_cmd_run_sync(*cmd);
1264 cmd->count = 0;
1265 return p;
1266}
1267
1269{
1270 bool p = nob_cmd_run_sync_redirect(*cmd, redirect);
1271 cmd->count = 0;
1272 if (redirect.fdin) {
1273 nob_fd_close(*redirect.fdin);
1274 *redirect.fdin = NOB_INVALID_FD;
1275 }
1276 if (redirect.fdout) {
1277 nob_fd_close(*redirect.fdout);
1278 *redirect.fdout = NOB_INVALID_FD;
1279 }
1280 if (redirect.fderr) {
1281 nob_fd_close(*redirect.fderr);
1282 *redirect.fderr = NOB_INVALID_FD;
1283 }
1284 return p;
1285}
1286
1287NOBDEF void nob_log(Nob_Log_Level level, const char *fmt, ...)
1288{
1289 if (level < nob_minimal_log_level) return;
1290
1291 switch (level) {
1292 case NOB_INFO:
1293 fprintf(stderr, "[INFO] ");
1294 break;
1295 case NOB_WARNING:
1296 fprintf(stderr, "[WARNING] ");
1297 break;
1298 case NOB_ERROR:
1299 fprintf(stderr, "[ERROR] ");
1300 break;
1301 case NOB_NO_LOGS: return;
1302 default:
1303 NOB_UNREACHABLE("nob_log");
1304 }
1305
1306 va_list args;
1307 va_start(args, fmt);
1308 vfprintf(stderr, fmt, args);
1309 va_end(args);
1310 fprintf(stderr, "\n");
1311}
1312
1313NOBDEF bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children)
1314{
1315 bool result = true;
1316 DIR *dir = NULL;
1317
1318 dir = opendir(parent);
1319 if (dir == NULL) {
1320 #ifdef _WIN32
1321 nob_log(NOB_ERROR, "Could not open directory %s: %s", parent, nob_win32_error_message(GetLastError()));
1322 #else
1323 nob_log(NOB_ERROR, "Could not open directory %s: %s", parent, strerror(errno));
1324 #endif // _WIN32
1325 nob_return_defer(false);
1326 }
1327
1328 errno = 0;
1329 struct dirent *ent = readdir(dir);
1330 while (ent != NULL) {
1331 nob_da_append(children, nob_temp_strdup(ent->d_name));
1332 ent = readdir(dir);
1333 }
1334
1335 if (errno != 0) {
1336 #ifdef _WIN32
1337 nob_log(NOB_ERROR, "Could not read directory %s: %s", parent, nob_win32_error_message(GetLastError()));
1338 #else
1339 nob_log(NOB_ERROR, "Could not read directory %s: %s", parent, strerror(errno));
1340 #endif // _WIN32
1341 nob_return_defer(false);
1342 }
1343
1344defer:
1345 if (dir) closedir(dir);
1346 return result;
1347}
1348
1349NOBDEF bool nob_write_entire_file(const char *path, const void *data, size_t size)
1350{
1351 bool result = true;
1352
1353 FILE *f = fopen(path, "wb");
1354 if (f == NULL) {
1355 nob_log(NOB_ERROR, "Could not open file %s for writing: %s\n", path, strerror(errno));
1356 nob_return_defer(false);
1357 }
1358
1359 // len
1360 // v
1361 // aaaaaaaaaa
1362 // ^
1363 // data
1364
1365 const char *buf = data;
1366 while (size > 0) {
1367 size_t n = fwrite(buf, 1, size, f);
1368 if (ferror(f)) {
1369 nob_log(NOB_ERROR, "Could not write into file %s: %s\n", path, strerror(errno));
1370 nob_return_defer(false);
1371 }
1372 size -= n;
1373 buf += n;
1374 }
1375
1376defer:
1377 if (f) fclose(f);
1378 return result;
1379}
1380
1381NOBDEF Nob_File_Type nob_get_file_type(const char *path)
1382{
1383#ifdef _WIN32
1384 DWORD attr = GetFileAttributesA(path);
1385 if (attr == INVALID_FILE_ATTRIBUTES) {
1386 nob_log(NOB_ERROR, "Could not get file attributes of %s: %s", path, nob_win32_error_message(GetLastError()));
1387 return -1;
1388 }
1389
1390 if (attr & FILE_ATTRIBUTE_DIRECTORY) return NOB_FILE_DIRECTORY;
1391 // TODO: detect symlinks on Windows (whatever that means on Windows anyway)
1392 return NOB_FILE_REGULAR;
1393#else // _WIN32
1394 struct stat statbuf;
1395 if (lstat(path, &statbuf) < 0) {
1396 nob_log(NOB_ERROR, "Could not get stat of %s: %s", path, strerror(errno));
1397 return -1;
1398 }
1399
1400 if (S_ISREG(statbuf.st_mode)) return NOB_FILE_REGULAR;
1401 if (S_ISDIR(statbuf.st_mode)) return NOB_FILE_DIRECTORY;
1402 if (S_ISLNK(statbuf.st_mode)) return NOB_FILE_SYMLINK;
1403 return NOB_FILE_OTHER;
1404#endif // _WIN32
1405}
1406
1407NOBDEF bool nob_delete_file(const char *path)
1408{
1409 nob_log(NOB_INFO, "deleting %s", path);
1410#ifdef _WIN32
1411 if (!DeleteFileA(path)) {
1412 nob_log(NOB_ERROR, "Could not delete file %s: %s", path, nob_win32_error_message(GetLastError()));
1413 return false;
1414 }
1415 return true;
1416#else
1417 if (remove(path) < 0) {
1418 nob_log(NOB_ERROR, "Could not delete file %s: %s", path, strerror(errno));
1419 return false;
1420 }
1421 return true;
1422#endif // _WIN32
1423}
1424
1425NOBDEF bool nob_copy_directory_recursively(const char *src_path, const char *dst_path)
1426{
1427 bool result = true;
1428 Nob_File_Paths children = {0};
1429 Nob_String_Builder src_sb = {0};
1430 Nob_String_Builder dst_sb = {0};
1431 size_t temp_checkpoint = nob_temp_save();
1432
1433 Nob_File_Type type = nob_get_file_type(src_path);
1434 if (type < 0) return false;
1435
1436 switch (type) {
1437 case NOB_FILE_DIRECTORY: {
1438 if (!nob_mkdir_if_not_exists(dst_path)) nob_return_defer(false);
1439 if (!nob_read_entire_dir(src_path, &children)) nob_return_defer(false);
1440
1441 for (size_t i = 0; i < children.count; ++i) {
1442 if (strcmp(children.items[i], ".") == 0) continue;
1443 if (strcmp(children.items[i], "..") == 0) continue;
1444
1445 src_sb.count = 0;
1446 nob_sb_append_cstr(&src_sb, src_path);
1447 nob_sb_append_cstr(&src_sb, "/");
1448 nob_sb_append_cstr(&src_sb, children.items[i]);
1449 nob_sb_append_null(&src_sb);
1450
1451 dst_sb.count = 0;
1452 nob_sb_append_cstr(&dst_sb, dst_path);
1453 nob_sb_append_cstr(&dst_sb, "/");
1454 nob_sb_append_cstr(&dst_sb, children.items[i]);
1455 nob_sb_append_null(&dst_sb);
1456
1457 if (!nob_copy_directory_recursively(src_sb.items, dst_sb.items)) {
1458 nob_return_defer(false);
1459 }
1460 }
1461 } break;
1462
1463 case NOB_FILE_REGULAR: {
1464 if (!nob_copy_file(src_path, dst_path)) {
1465 nob_return_defer(false);
1466 }
1467 } break;
1468
1469 case NOB_FILE_SYMLINK: {
1470 nob_log(NOB_WARNING, "TODO: Copying symlinks is not supported yet");
1471 } break;
1472
1473 case NOB_FILE_OTHER: {
1474 nob_log(NOB_ERROR, "Unsupported type of file %s", src_path);
1475 nob_return_defer(false);
1476 } break;
1477
1478 default: NOB_UNREACHABLE("nob_copy_directory_recursively");
1479 }
1480
1481defer:
1482 nob_temp_rewind(temp_checkpoint);
1483 nob_da_free(src_sb);
1484 nob_da_free(dst_sb);
1485 nob_da_free(children);
1486 return result;
1487}
1488
1489NOBDEF char *nob_temp_strdup(const char *cstr)
1490{
1491 size_t n = strlen(cstr);
1492 char *result = nob_temp_alloc(n + 1);
1493 NOB_ASSERT(result != NULL && "Increase NOB_TEMP_CAPACITY");
1494 memcpy(result, cstr, n);
1495 result[n] = '\0';
1496 return result;
1497}
1498
1499NOBDEF void *nob_temp_alloc(size_t requested_size)
1500{
1501 size_t word_size = sizeof(uintptr_t);
1502 size_t size = (requested_size + word_size - 1)/word_size*word_size;
1503 if (nob_temp_size + size > NOB_TEMP_CAPACITY) return NULL;
1504 void *result = &nob_temp[nob_temp_size];
1505 nob_temp_size += size;
1506 return result;
1507}
1508
1509NOBDEF char *nob_temp_sprintf(const char *format, ...)
1510{
1511 va_list args;
1512 va_start(args, format);
1513 int n = vsnprintf(NULL, 0, format, args);
1514 va_end(args);
1515
1516 NOB_ASSERT(n >= 0);
1517 char *result = nob_temp_alloc(n + 1);
1518 NOB_ASSERT(result != NULL && "Extend the size of the temporary allocator");
1519 // TODO: use proper arenas for the temporary allocator;
1520 va_start(args, format);
1521 vsnprintf(result, n + 1, format, args);
1522 va_end(args);
1523
1524 return result;
1525}
1526
1527NOBDEF void nob_temp_reset(void)
1528{
1529 nob_temp_size = 0;
1530}
1531
1532NOBDEF size_t nob_temp_save(void)
1533{
1534 return nob_temp_size;
1535}
1536
1537NOBDEF void nob_temp_rewind(size_t checkpoint)
1538{
1539 nob_temp_size = checkpoint;
1540}
1541
1543{
1544 char *result = nob_temp_alloc(sv.count + 1);
1545 NOB_ASSERT(result != NULL && "Extend the size of the temporary allocator");
1546 memcpy(result, sv.data, sv.count);
1547 result[sv.count] = '\0';
1548 return result;
1549}
1550
1551NOBDEF int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count)
1552{
1553#ifdef _WIN32
1554 BOOL bSuccess;
1555
1556 HANDLE output_path_fd = CreateFile(output_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
1557 if (output_path_fd == INVALID_HANDLE_VALUE) {
1558 // NOTE: if output does not exist it 100% must be rebuilt
1559 if (GetLastError() == ERROR_FILE_NOT_FOUND) return 1;
1560 nob_log(NOB_ERROR, "Could not open file %s: %s", output_path, nob_win32_error_message(GetLastError()));
1561 return -1;
1562 }
1563 FILETIME output_path_time;
1564 bSuccess = GetFileTime(output_path_fd, NULL, NULL, &output_path_time);
1565 CloseHandle(output_path_fd);
1566 if (!bSuccess) {
1567 nob_log(NOB_ERROR, "Could not get time of %s: %s", output_path, nob_win32_error_message(GetLastError()));
1568 return -1;
1569 }
1570
1571 for (size_t i = 0; i < input_paths_count; ++i) {
1572 const char *input_path = input_paths[i];
1573 HANDLE input_path_fd = CreateFile(input_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
1574 if (input_path_fd == INVALID_HANDLE_VALUE) {
1575 // NOTE: non-existing input is an error cause it is needed for building in the first place
1576 nob_log(NOB_ERROR, "Could not open file %s: %s", input_path, nob_win32_error_message(GetLastError()));
1577 return -1;
1578 }
1579 FILETIME input_path_time;
1580 bSuccess = GetFileTime(input_path_fd, NULL, NULL, &input_path_time);
1581 CloseHandle(input_path_fd);
1582 if (!bSuccess) {
1583 nob_log(NOB_ERROR, "Could not get time of %s: %s", input_path, nob_win32_error_message(GetLastError()));
1584 return -1;
1585 }
1586
1587 // NOTE: if even a single input_path is fresher than output_path that's 100% rebuild
1588 if (CompareFileTime(&input_path_time, &output_path_time) == 1) return 1;
1589 }
1590
1591 return 0;
1592#else
1593 struct stat statbuf = {0};
1594
1595 if (stat(output_path, &statbuf) < 0) {
1596 // NOTE: if output does not exist it 100% must be rebuilt
1597 if (errno == ENOENT) return 1;
1598 nob_log(NOB_ERROR, "could not stat %s: %s", output_path, strerror(errno));
1599 return -1;
1600 }
1601 int output_path_time = statbuf.st_mtime;
1602
1603 for (size_t i = 0; i < input_paths_count; ++i) {
1604 const char *input_path = input_paths[i];
1605 if (stat(input_path, &statbuf) < 0) {
1606 // NOTE: non-existing input is an error cause it is needed for building in the first place
1607 nob_log(NOB_ERROR, "could not stat %s: %s", input_path, strerror(errno));
1608 return -1;
1609 }
1610 int input_path_time = statbuf.st_mtime;
1611 // NOTE: if even a single input_path is fresher than output_path that's 100% rebuild
1612 if (input_path_time > output_path_time) return 1;
1613 }
1614
1615 return 0;
1616#endif
1617}
1618
1619NOBDEF int nob_needs_rebuild1(const char *output_path, const char *input_path)
1620{
1621 return nob_needs_rebuild(output_path, &input_path, 1);
1622}
1623
1624NOBDEF const char *nob_path_name(const char *path)
1625{
1626#ifdef _WIN32
1627 const char *p1 = strrchr(path, '/');
1628 const char *p2 = strrchr(path, '\\');
1629 const char *p = (p1 > p2)? p1 : p2; // NULL is ignored if the other search is successful
1630 return p ? p + 1 : path;
1631#else
1632 const char *p = strrchr(path, '/');
1633 return p ? p + 1 : path;
1634#endif // _WIN32
1635}
1636
1637NOBDEF bool nob_rename(const char *old_path, const char *new_path)
1638{
1639 nob_log(NOB_INFO, "renaming %s -> %s", old_path, new_path);
1640#ifdef _WIN32
1641 if (!MoveFileEx(old_path, new_path, MOVEFILE_REPLACE_EXISTING)) {
1642 nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, nob_win32_error_message(GetLastError()));
1643 return false;
1644 }
1645#else
1646 if (rename(old_path, new_path) < 0) {
1647 nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, strerror(errno));
1648 return false;
1649 }
1650#endif // _WIN32
1651 return true;
1652}
1653
1654NOBDEF bool nob_read_entire_file(const char *path, Nob_String_Builder *sb)
1655{
1656 bool result = true;
1657
1658 FILE *f = fopen(path, "rb");
1659 if (f == NULL) nob_return_defer(false);
1660 if (fseek(f, 0, SEEK_END) < 0) nob_return_defer(false);
1661#ifndef _WIN32
1662 long m = ftell(f);
1663#else
1664 long long m = _ftelli64(f);
1665#endif
1666 if (m < 0) nob_return_defer(false);
1667 if (fseek(f, 0, SEEK_SET) < 0) nob_return_defer(false);
1668
1669 size_t new_count = sb->count + m;
1670 if (new_count > sb->capacity) {
1671 sb->items = NOB_REALLOC(sb->items, new_count);
1672 NOB_ASSERT(sb->items != NULL && "Buy more RAM lool!!");
1673 sb->capacity = new_count;
1674 }
1675
1676 fread(sb->items + sb->count, m, 1, f);
1677 if (ferror(f)) {
1678 // TODO: Afaik, ferror does not set errno. So the error reporting in defer is not correct in this case.
1679 nob_return_defer(false);
1680 }
1681 sb->count = new_count;
1682
1683defer:
1684 if (!result) nob_log(NOB_ERROR, "Could not read file %s: %s", path, strerror(errno));
1685 if (f) fclose(f);
1686 return result;
1687}
1688
1689NOBDEF int nob_sb_appendf(Nob_String_Builder *sb, const char *fmt, ...)
1690{
1691 va_list args;
1692
1693 va_start(args, fmt);
1694 int n = vsnprintf(NULL, 0, fmt, args);
1695 va_end(args);
1696
1697 // NOTE: the new_capacity needs to be +1 because of the null terminator.
1698 // However, further below we increase sb->count by n, not n + 1.
1699 // This is because we don't want the sb to include the null terminator. The user can always sb_append_null() if they want it
1700 nob_da_reserve(sb, sb->count + n + 1);
1701 char *dest = sb->items + sb->count;
1702 va_start(args, fmt);
1703 vsnprintf(dest, n+1, fmt, args);
1704 va_end(args);
1705
1706 sb->count += n;
1707
1708 return n;
1709}
1710
1712{
1713 size_t i = 0;
1714 while (i < sv->count && sv->data[i] != delim) {
1715 i += 1;
1716 }
1717
1718 Nob_String_View result = nob_sv_from_parts(sv->data, i);
1719
1720 if (i < sv->count) {
1721 sv->count -= i + 1;
1722 sv->data += i + 1;
1723 } else {
1724 sv->count -= i;
1725 sv->data += i;
1726 }
1727
1728 return result;
1729}
1730
1732{
1733 if (n > sv->count) {
1734 n = sv->count;
1735 }
1736
1737 Nob_String_View result = nob_sv_from_parts(sv->data, n);
1738
1739 sv->data += n;
1740 sv->count -= n;
1741
1742 return result;
1743}
1744
1745NOBDEF Nob_String_View nob_sv_from_parts(const char *data, size_t count)
1746{
1747 Nob_String_View sv;
1748 sv.count = count;
1749 sv.data = data;
1750 return sv;
1751}
1752
1754{
1755 size_t i = 0;
1756 while (i < sv.count && isspace(sv.data[i])) {
1757 i += 1;
1758 }
1759
1760 return nob_sv_from_parts(sv.data + i, sv.count - i);
1761}
1762
1764{
1765 size_t i = 0;
1766 while (i < sv.count && isspace(sv.data[sv.count - 1 - i])) {
1767 i += 1;
1768 }
1769
1770 return nob_sv_from_parts(sv.data, sv.count - i);
1771}
1772
1774{
1776}
1777
1778NOBDEF Nob_String_View nob_sv_from_cstr(const char *cstr)
1779{
1780 return nob_sv_from_parts(cstr, strlen(cstr));
1781}
1782
1784{
1785 if (a.count != b.count) {
1786 return false;
1787 } else {
1788 return memcmp(a.data, b.data, a.count) == 0;
1789 }
1790}
1791
1792NOBDEF bool nob_sv_end_with(Nob_String_View sv, const char *cstr)
1793{
1794 size_t cstr_count = strlen(cstr);
1795 if (sv.count >= cstr_count) {
1796 size_t ending_start = sv.count - cstr_count;
1797 Nob_String_View sv_ending = nob_sv_from_parts(sv.data + ending_start, cstr_count);
1798 return nob_sv_eq(sv_ending, nob_sv_from_cstr(cstr));
1799 }
1800 return false;
1801}
1802
1803
1805{
1806 if (expected_prefix.count <= sv.count) {
1807 Nob_String_View actual_prefix = nob_sv_from_parts(sv.data, expected_prefix.count);
1808 return nob_sv_eq(expected_prefix, actual_prefix);
1809 }
1810
1811 return false;
1812}
1813
1814// RETURNS:
1815// 0 - file does not exists
1816// 1 - file exists
1817// -1 - error while checking if file exists. The error is logged
1818NOBDEF int nob_file_exists(const char *file_path)
1819{
1820#if _WIN32
1821 // TODO: distinguish between "does not exists" and other errors
1822 DWORD dwAttrib = GetFileAttributesA(file_path);
1823 return dwAttrib != INVALID_FILE_ATTRIBUTES;
1824#else
1825 struct stat statbuf;
1826 if (stat(file_path, &statbuf) < 0) {
1827 if (errno == ENOENT) return 0;
1828 nob_log(NOB_ERROR, "Could not check if file %s exists: %s", file_path, strerror(errno));
1829 return -1;
1830 }
1831 return 1;
1832#endif
1833}
1834
1835NOBDEF const char *nob_get_current_dir_temp(void)
1836{
1837#ifdef _WIN32
1838 DWORD nBufferLength = GetCurrentDirectory(0, NULL);
1839 if (nBufferLength == 0) {
1840 nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError()));
1841 return NULL;
1842 }
1843
1844 char *buffer = (char*) nob_temp_alloc(nBufferLength);
1845 if (GetCurrentDirectory(nBufferLength, buffer) == 0) {
1846 nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError()));
1847 return NULL;
1848 }
1849
1850 return buffer;
1851#else
1852 char *buffer = (char*) nob_temp_alloc(PATH_MAX);
1853 if (getcwd(buffer, PATH_MAX) == NULL) {
1854 nob_log(NOB_ERROR, "could not get current directory: %s", strerror(errno));
1855 return NULL;
1856 }
1857
1858 return buffer;
1859#endif // _WIN32
1860}
1861
1862NOBDEF bool nob_set_current_dir(const char *path)
1863{
1864#ifdef _WIN32
1865 if (!SetCurrentDirectory(path)) {
1866 nob_log(NOB_ERROR, "could not set current directory to %s: %s", path, nob_win32_error_message(GetLastError()));
1867 return false;
1868 }
1869 return true;
1870#else
1871 if (chdir(path) < 0) {
1872 nob_log(NOB_ERROR, "could not set current directory to %s: %s", path, strerror(errno));
1873 return false;
1874 }
1875 return true;
1876#endif // _WIN32
1877}
1878
1879// minirent.h SOURCE BEGIN ////////////////////////////////////////
1880#if defined(_WIN32) && !defined(NOB_NO_MINIRENT)
1881struct DIR
1882{
1883 HANDLE hFind;
1884 WIN32_FIND_DATA data;
1885 struct dirent *dirent;
1886};
1887
1888NOBDEF DIR *opendir(const char *dirpath)
1889{
1890 NOB_ASSERT(dirpath);
1891
1892 char buffer[MAX_PATH];
1893 snprintf(buffer, MAX_PATH, "%s\\*", dirpath);
1894
1895 DIR *dir = (DIR*)NOB_REALLOC(NULL, sizeof(DIR));
1896 memset(dir, 0, sizeof(DIR));
1897
1898 dir->hFind = FindFirstFile(buffer, &dir->data);
1899 if (dir->hFind == INVALID_HANDLE_VALUE) {
1900 // TODO: opendir should set errno accordingly on FindFirstFile fail
1901 // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
1902 errno = ENOSYS;
1903 goto fail;
1904 }
1905
1906 return dir;
1907
1908fail:
1909 if (dir) {
1910 NOB_FREE(dir);
1911 }
1912
1913 return NULL;
1914}
1915
1916NOBDEF struct dirent *readdir(DIR *dirp)
1917{
1918 NOB_ASSERT(dirp);
1919
1920 if (dirp->dirent == NULL) {
1921 dirp->dirent = (struct dirent*)NOB_REALLOC(NULL, sizeof(struct dirent));
1922 memset(dirp->dirent, 0, sizeof(struct dirent));
1923 } else {
1924 if(!FindNextFile(dirp->hFind, &dirp->data)) {
1925 if (GetLastError() != ERROR_NO_MORE_FILES) {
1926 // TODO: readdir should set errno accordingly on FindNextFile fail
1927 // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
1928 errno = ENOSYS;
1929 }
1930
1931 return NULL;
1932 }
1933 }
1934
1935 memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name));
1936
1937 strncpy(
1938 dirp->dirent->d_name,
1939 dirp->data.cFileName,
1940 sizeof(dirp->dirent->d_name) - 1);
1941
1942 return dirp->dirent;
1943}
1944
1945NOBDEF int closedir(DIR *dirp)
1946{
1947 NOB_ASSERT(dirp);
1948
1949 if(!FindClose(dirp->hFind)) {
1950 // TODO: closedir should set errno accordingly on FindClose fail
1951 // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
1952 errno = ENOSYS;
1953 return -1;
1954 }
1955
1956 if (dirp->dirent) {
1957 NOB_FREE(dirp->dirent);
1958 }
1959 NOB_FREE(dirp);
1960
1961 return 0;
1962}
1963#endif // _WIN32
1964// minirent.h SOURCE END ////////////////////////////////////////
1965
1966#endif // NOB_IMPLEMENTATION
1967
1968#ifndef NOB_STRIP_PREFIX_GUARD_
1969#define NOB_STRIP_PREFIX_GUARD_
1970 // NOTE: The name stripping should be part of the header so it's not accidentally included
1971 // several times. At the same time, it should be at the end of the file so to not create any
1972 // potential conflicts in the NOB_IMPLEMENTATION. The header obviously cannot be at the end
1973 // of the file because NOB_IMPLEMENTATION needs the forward declarations from there. So the
1974 // solution is to split the header into two parts where the name stripping part is at the
1975 // end of the file after the NOB_IMPLEMENTATION.
1976 #ifdef NOB_STRIP_PREFIX
1977 #define TODO NOB_TODO
1978 #define UNREACHABLE NOB_UNREACHABLE
1979 #define UNUSED NOB_UNUSED
1980 #define ARRAY_LEN NOB_ARRAY_LEN
1981 #define ARRAY_GET NOB_ARRAY_GET
1982 #define INFO NOB_INFO
1983 #define WARNING NOB_WARNING
1984 #define ERROR NOB_ERROR
1985 #define NO_LOGS NOB_NO_LOGS
1986 #define Log_Level Nob_Log_Level
1987 #define minimal_log_level nob_minimal_log_level
1988 // NOTE: Name log is already defined in math.h and historically always was the natural logarithmic function.
1989 // So there should be no reason to strip the `nob_` prefix in this specific case.
1990 // #define log nob_log
1991 #define shift nob_shift
1992 #define shift_args nob_shift_args
1993 #define File_Paths Nob_File_Paths
1994 #define FILE_REGULAR NOB_FILE_REGULAR
1995 #define FILE_DIRECTORY NOB_FILE_DIRECTORY
1996 #define FILE_SYMLINK NOB_FILE_SYMLINK
1997 #define FILE_OTHER NOB_FILE_OTHER
1998 #define File_Type Nob_File_Type
1999 #define mkdir_if_not_exists nob_mkdir_if_not_exists
2000 #define copy_file nob_copy_file
2001 #define copy_directory_recursively nob_copy_directory_recursively
2002 #define read_entire_dir nob_read_entire_dir
2003 #define write_entire_file nob_write_entire_file
2004 #define get_file_type nob_get_file_type
2005 #define delete_file nob_delete_file
2006 #define return_defer nob_return_defer
2007 #define da_append nob_da_append
2008 #define da_free nob_da_free
2009 #define da_append_many nob_da_append_many
2010 #define da_resize nob_da_resize
2011 #define da_reserve nob_da_reserve
2012 #define da_last nob_da_last
2013 #define da_remove_unordered nob_da_remove_unordered
2014 #define da_foreach nob_da_foreach
2015 #define String_Builder Nob_String_Builder
2016 #define read_entire_file nob_read_entire_file
2017 #define sb_appendf nob_sb_appendf
2018 #define sb_append_buf nob_sb_append_buf
2019 #define sb_append_cstr nob_sb_append_cstr
2020 #define sb_append_null nob_sb_append_null
2021 #define sb_free nob_sb_free
2022 #define Proc Nob_Proc
2023 #define INVALID_PROC NOB_INVALID_PROC
2024 #define Fd Nob_Fd
2025 #define INVALID_FD NOB_INVALID_FD
2026 #define fd_open_for_read nob_fd_open_for_read
2027 #define fd_open_for_write nob_fd_open_for_write
2028 #define fd_close nob_fd_close
2029 #define Procs Nob_Procs
2030 #define proc_wait nob_proc_wait
2031 #define procs_wait nob_procs_wait
2032 #define procs_wait_and_reset nob_procs_wait_and_reset
2033 #define procs_append_with_flush nob_procs_append_with_flush
2034 #define Cmd Nob_Cmd
2035 #define Cmd_Redirect Nob_Cmd_Redirect
2036 #define cmd_render nob_cmd_render
2037 #define cmd_append nob_cmd_append
2038 #define cmd_extend nob_cmd_extend
2039 #define cmd_free nob_cmd_free
2040 #define cmd_run_async nob_cmd_run_async
2041 #define cmd_run_async_and_reset nob_cmd_run_async_and_reset
2042 #define cmd_run_async_redirect nob_cmd_run_async_redirect
2043 #define cmd_run_async_redirect_and_reset nob_cmd_run_async_redirect_and_reset
2044 #define cmd_run_sync nob_cmd_run_sync
2045 #define cmd_run_sync_and_reset nob_cmd_run_sync_and_reset
2046 #define cmd_run_sync_redirect nob_cmd_run_sync_redirect
2047 #define cmd_run_sync_redirect_and_reset nob_cmd_run_sync_redirect_and_reset
2048 #define temp_strdup nob_temp_strdup
2049 #define temp_alloc nob_temp_alloc
2050 #define temp_sprintf nob_temp_sprintf
2051 #define temp_reset nob_temp_reset
2052 #define temp_save nob_temp_save
2053 #define temp_rewind nob_temp_rewind
2054 #define path_name nob_path_name
2055 // NOTE: rename(2) is widely known POSIX function. We never wanna collide with it.
2056 // #define rename nob_rename
2057 #define needs_rebuild nob_needs_rebuild
2058 #define needs_rebuild1 nob_needs_rebuild1
2059 #define file_exists nob_file_exists
2060 #define get_current_dir_temp nob_get_current_dir_temp
2061 #define set_current_dir nob_set_current_dir
2062 #define String_View Nob_String_View
2063 #define temp_sv_to_cstr nob_temp_sv_to_cstr
2064 #define sv_chop_by_delim nob_sv_chop_by_delim
2065 #define sv_chop_left nob_sv_chop_left
2066 #define sv_trim nob_sv_trim
2067 #define sv_trim_left nob_sv_trim_left
2068 #define sv_trim_right nob_sv_trim_right
2069 #define sv_eq nob_sv_eq
2070 #define sv_starts_with nob_sv_starts_with
2071 #define sv_end_with nob_sv_end_with
2072 #define sv_from_cstr nob_sv_from_cstr
2073 #define sv_from_parts nob_sv_from_parts
2074 #define sb_to_sv nob_sb_to_sv
2075 #define win32_error_message nob_win32_error_message
2076 #endif // NOB_STRIP_PREFIX
2077#endif // NOB_STRIP_PREFIX_GUARD_
2078
2079/*
2080 Revision history:
2081
2082 1.22.0 (2025-08-12) Add NOBDEF macro to the beginning of function declarations (by @minefreak19)
2083 Add more flags to MSVC nob_cc_flags() (by @PieVieRo)
2084 1.21.0 (2025-08-11) Add NOB_NO_MINIRENT guard for "minirent.h" (by @fietec)
2085 1.20.9 (2025-08-11) Fix warnings on Windows: Define _CRT_SECURE_NO_WARNINGS, Rename mkdir to _mkdir (by @OetkenPurveyorOfCode)
2086 1.20.8 (2025-08-11) Fix the bug with nob_get_file_type() not identifying symlinks correctly on POSIX (By @samuellieberman)
2087 1.20.7 (2025-07-29) Align nob_temp_alloc() allocations by the word size (By @rexim)
2088 1.20.6 (2025-05-16) Never strip nob_* suffix from nob_rename (By @rexim)
2089 1.20.5 (2025-05-16) NOB_PRINTF_FORMAT() support for MinGW (By @KillerxDBr)
2090 1.20.4 (2025-05-16) More reliable rendering of the Windows command (By @vylsaz)
2091 1.20.3 (2025-05-16) Add check for __clang__ along with _MSC_VER checks (By @nashiora)
2092 1.20.2 (2025-04-24) Report the program name that failed to start up in nob_cmd_run_async_redirect() (By @rexim)
2093 1.20.1 (2025-04-16) Use vsnprintf() in nob_sb_appendf() instead of vsprintf() (By @LainLayer)
2094 1.20.0 (2025-04-16) Introduce nob_cc(), nob_cc_flags(), nob_cc_inputs(), nob_cc_output() macros (By @rexim)
2095 1.19.0 (2025-03-25) Add nob_procs_append_with_flush() (By @rexim and @anion155)
2096 1.18.0 (2025-03-24) Add nob_da_foreach() (By @rexim)
2097 Allow file sizes greater than 2GB to be read on windows (By @satchelfrost and @KillerxDBr)
2098 Fix nob_fd_open_for_write behaviour on windows so it truncates the opened files (By @twixuss)
2099 1.17.0 (2025-03-16) Factor out nob_da_reserve() (By @rexim)
2100 Add nob_sb_appendf() (By @angelcaru)
2101 1.16.1 (2025-03-16) Make nob_da_resize() exponentially grow capacity similar to no_da_append_many()
2102 1.16.0 (2025-03-16) Introduce NOB_PRINTF_FORMAT
2103 1.15.1 (2025-03-16) Make nob.h compilable in gcc/clang with -std=c99 on POSIX. This includes:
2104 not using strsignal()
2105 using S_IS* stat macros instead of S_IF* flags
2106 1.15.0 (2025-03-03) Add nob_sv_chop_left()
2107 1.14.1 (2025-03-02) Add NOB_EXPERIMENTAL_DELETE_OLD flag that enables deletion of nob.old in Go Rebuild Urself™ Technology
2108 1.14.0 (2025-02-17) Add nob_da_last()
2109 Add nob_da_remove_unordered()
2110 1.13.1 (2025-02-17) Fix segfault in nob_delete_file() (By @SileNce5k)
2111 1.13.0 (2025-02-11) Add nob_da_resize() (By @satchelfrost)
2112 1.12.0 (2025-02-04) Add nob_delete_file()
2113 Add nob_sv_start_with()
2114 1.11.0 (2025-02-04) Add NOB_GO_REBUILD_URSELF_PLUS() (By @rexim)
2115 1.10.0 (2025-02-04) Make NOB_ASSERT, NOB_REALLOC, and NOB_FREE redefinable (By @OleksiiBulba)
2116 1.9.1 (2025-02-04) Fix signature of nob_get_current_dir_temp() (By @julianstoerig)
2117 1.9.0 (2024-11-06) Add Nob_Cmd_Redirect mechanism (By @rexim)
2118 Add nob_path_name() (By @0dminnimda)
2119 1.8.0 (2024-11-03) Add nob_cmd_extend() (By @0dminnimda)
2120 1.7.0 (2024-11-03) Add nob_win32_error_message and NOB_WIN32_ERR_MSG_SIZE (By @KillerxDBr)
2121 1.6.0 (2024-10-27) Add nob_cmd_run_sync_and_reset()
2122 Add nob_sb_to_sv()
2123 Add nob_procs_wait_and_reset()
2124 1.5.1 (2024-10-25) Include limits.h for Linux musl libc (by @pgalkin)
2125 1.5.0 (2024-10-23) Add nob_get_current_dir_temp()
2126 Add nob_set_current_dir()
2127 1.4.0 (2024-10-21) Fix UX issues with NOB_GO_REBUILD_URSELF on Windows when you call nob without the .exe extension (By @pgalkin)
2128 Add nob_sv_end_with (By @pgalkin)
2129 1.3.2 (2024-10-21) Fix unreachable error in nob_log on passing NOB_NO_LOGS
2130 1.3.1 (2024-10-21) Fix redeclaration error for minimal_log_level (By @KillerxDBr)
2131 1.3.0 (2024-10-17) Add NOB_UNREACHABLE
2132 1.2.2 (2024-10-16) Fix compilation of nob_cmd_run_sync_and_reset on Windows (By @KillerxDBr)
2133 1.2.1 (2024-10-16) Add a separate include guard for NOB_STRIP_PREFIX.
2134 1.2.0 (2024-10-15) Make NOB_DA_INIT_CAP redefinable
2135 Add NOB_STRIP_PREFIX which strips off nob_* prefix from all the user facing names
2136 Add NOB_UNUSED macro
2137 Add NOB_TODO macro
2138 Add nob_sv_trim_left and nob_sv_trim_right declarations to the header part
2139 1.1.1 (2024-10-15) Remove forward declaration for is_path1_modified_after_path2
2140 1.1.0 (2024-10-15) nob_minimal_log_level
2141 nob_cmd_run_sync_and_reset
2142 1.0.0 (2024-10-15) first release based on https://github.com/tsoding/musializer/blob/4ac7cce9874bc19e02d8c160c8c6229de8919401/nob.h
2143*/
2144
2145/*
2146 Version Conventions:
2147
2148 We are following https://semver.org/ so the version has a format MAJOR.MINOR.PATCH:
2149 - Modifying comments does not update the version.
2150 - PATCH is incremented in case of a bug fix or refactoring without touching the API.
2151 - MINOR is incremented when new functions and/or types are added in a way that does
2152 not break any existing user code. We want to do this in the majority of the situation.
2153 If we want to delete a certain function or type in favor of another one we should
2154 just add the new function/type and deprecate the old one in a backward compatible way
2155 and let them co-exist for a while.
2156 - MAJOR update should be just a periodic cleanup of the deprecated functions and types
2157 without really modifying any existing functionality.
2158
2159 Naming Conventions:
2160
2161 - All the user facing names should be prefixed with `nob_` or `NOB_` depending on the case.
2162 - The prefixes of non-redefinable names should be strippable with NOB_STRIP_PREFIX (unless
2163 explicitly stated otherwise like in case of nob_log).
2164 - Internal functions should be prefixed with `nob__` (double underscore).
2165*/
2166
2167/*
2168 ------------------------------------------------------------------------------
2169 This software is available under 2 licenses -- choose whichever you prefer.
2170 ------------------------------------------------------------------------------
2171 ALTERNATIVE A - MIT License
2172 Copyright (c) 2024 Alexey Kutepov
2173 Permission is hereby granted, free of charge, to any person obtaining a copy of
2174 this software and associated documentation files (the "Software"), to deal in
2175 the Software without restriction, including without limitation the rights to
2176 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
2177 of the Software, and to permit persons to whom the Software is furnished to do
2178 so, subject to the following conditions:
2179 The above copyright notice and this permission notice shall be included in all
2180 copies or substantial portions of the Software.
2181 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2182 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2183 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2184 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2185 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2186 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2187 SOFTWARE.
2188 ------------------------------------------------------------------------------
2189 ALTERNATIVE B - Public Domain (www.unlicense.org)
2190 This is free and unencumbered software released into the public domain.
2191 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
2192 software, either in source code form or as a compiled binary, for any purpose,
2193 commercial or non-commercial, and by any means.
2194 In jurisdictions that recognize copyright laws, the author or authors of this
2195 software dedicate any and all copyright interest in the software to the public
2196 domain. We make this dedication for the benefit of the public at large and to
2197 the detriment of our heirs and successors. We intend this dedication to be an
2198 overt act of relinquishment in perpetuity of all present and future rights to
2199 this software under copyright law.
2200 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2201 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2202 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2203 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2204 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
2205 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2206 ------------------------------------------------------------------------------
2207*/
NOBDEF bool nob_sv_end_with(Nob_String_View sv, const char *cstr)
#define nob_da_free(da)
Definition nob.h:317
#define nob_shift(xs, xs_sz)
Definition nob.h:261
#define nob_cmd_append(cmd,...)
Definition nob.h:455
NOBDEF bool nob_cmd_run_sync_redirect_and_reset(Nob_Cmd *cmd, Nob_Cmd_Redirect redirect)
NOBDEF Nob_String_View nob_sv_chop_left(Nob_String_View *sv, size_t n)
NOBDEF bool nob_procs_wait_and_reset(Nob_Procs *procs)
NOBDEF bool nob_set_current_dir(const char *path)
#define nob_sb_append_cstr(sb, cstr)
Definition nob.h:377
#define NOB_REBUILD_URSELF(binary_path, source_path)
Definition nob.h:583
NOBDEF int nob_file_exists(const char *file_path)
int Nob_Proc
Definition nob.h:398
#define NOB_FREE
Definition nob.h:191
NOBDEF Nob_Fd nob_fd_open_for_read(const char *path)
NOBDEF bool nob_delete_file(const char *path)
NOBDEF void nob__go_rebuild_urself(int argc, char **argv, const char *source_path,...)
NOBDEF bool nob_rename(const char *old_path, const char *new_path)
NOBDEF bool nob_copy_directory_recursively(const char *src_path, const char *dst_path)
NOBDEF int nob_needs_rebuild1(const char *output_path, const char *input_path)
NOBDEF bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children)
#define nob_da_reserve(da, expected_capacity)
Definition nob.h:296
NOBDEF void nob_fd_close(Nob_Fd fd)
NOBDEF char * nob_temp_strdup(const char *cstr)
NOBDEF bool nob_cmd_run_sync_and_reset(Nob_Cmd *cmd)
#define NOB_INVALID_FD
Definition nob.h:401
NOBDEF bool nob_sv_starts_with(Nob_String_View sv, Nob_String_View expected_prefix)
NOBDEF void nob_log(Nob_Log_Level level, const char *fmt,...) NOB_PRINTF_FORMAT(2
NOBDEF Nob_Proc nob_cmd_run_async_redirect_and_reset(Nob_Cmd *cmd, Nob_Cmd_Redirect redirect)
#define NOB_INVALID_PROC
Definition nob.h:399
NOBDEF Nob_String_View nob_sv_chop_by_delim(Nob_String_View *sv, char delim)
NOBDEF bool nob_proc_wait(Nob_Proc proc)
#define NOB_UNREACHABLE(message)
Definition nob.h:241
NOBDEF bool nob_procs_wait(Nob_Procs procs)
NOBDEF char NOBDEF void nob_temp_reset(void)
NOBDEF bool nob_write_entire_file(const char *path, const void *data, size_t size)
int Nob_Fd
Definition nob.h:400
NOBDEF char * nob_temp_sprintf(const char *format,...) NOB_PRINTF_FORMAT(1
NOBDEF Nob_String_View nob_sv_trim_right(Nob_String_View sv)
NOBDEF bool nob_cmd_run_sync_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect)
#define NOB_TEMP_CAPACITY
Definition nob.h:487
NOBDEF Nob_String_View nob_sv_from_cstr(const char *cstr)
NOBDEF bool nob_mkdir_if_not_exists(const char *path)
#define nob_return_defer(value)
Definition nob.h:289
NOBDEF bool nob_cmd_run_sync(Nob_Cmd cmd)
NOBDEF const char * nob_temp_sv_to_cstr(Nob_String_View sv)
#define nob_sb_free(sb)
Definition nob.h:389
Nob_File_Type
Definition nob.h:274
@ NOB_FILE_SYMLINK
Definition nob.h:277
@ NOB_FILE_DIRECTORY
Definition nob.h:276
@ NOB_FILE_REGULAR
Definition nob.h:275
@ NOB_FILE_OTHER
Definition nob.h:278
NOBDEF void nob_cmd_render(Nob_Cmd cmd, Nob_String_Builder *render)
NOBDEF int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count)
#define nob_da_append_many(da, new_items, new_items_count)
Definition nob.h:320
NOBDEF const char * nob_get_current_dir_temp(void)
Nob_Log_Level
Definition nob.h:247
@ NOB_WARNING
Definition nob.h:249
@ NOB_ERROR
Definition nob.h:250
@ NOB_NO_LOGS
Definition nob.h:251
@ NOB_INFO
Definition nob.h:248
#define nob_cmd_run_async(cmd)
Definition nob.h:467
#define NOB_REALLOC
Definition nob.h:186
#define NOBDEF
Definition nob.h:176
NOBDEF bool nob_sv_eq(Nob_String_View a, Nob_String_View b)
NOBDEF int nob_sb_appendf(Nob_String_Builder *sb, const char *fmt,...) NOB_PRINTF_FORMAT(2
NOBDEF Nob_File_Type nob_get_file_type(const char *path)
NOBDEF Nob_String_View nob_sv_from_parts(const char *data, size_t count)
#define NOB_PRINTF_FORMAT(STRING_INDEX, FIRST_TO_CHECK)
Definition nob.h:236
NOBDEF const char * nob_path_name(const char *path)
#define NOB_ASSERT
Definition nob.h:181
NOBDEF Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect)
NOBDEF Nob_Fd nob_fd_open_for_write(const char *path)
NOBDEF bool nob_copy_file(const char *src_path, const char *dst_path)
NOBDEF size_t nob_temp_save(void)
#define nob_sb_append_null(sb)
Definition nob.h:386
NOBDEF Nob_String_View nob_sv_trim_left(Nob_String_View sv)
#define nob_da_append(da, item)
Definition nob.h:311
NOBDEF void nob_temp_rewind(size_t checkpoint)
Nob_Log_Level nob_minimal_log_level
NOBDEF bool nob_procs_append_with_flush(Nob_Procs *procs, Nob_Proc proc, size_t max_procs_count)
NOBDEF bool nob_read_entire_file(const char *path, Nob_String_Builder *sb)
NOBDEF Nob_String_View nob_sv_trim(Nob_String_View sv)
NOBDEF void * nob_temp_alloc(size_t size)
NOBDEF Nob_Proc nob_cmd_run_async_and_reset(Nob_Cmd *cmd)
Definition nob.h:443
Nob_Fd * fdout
Definition nob.h:445
Nob_Fd * fderr
Definition nob.h:446
Nob_Fd * fdin
Definition nob.h:444
Definition nob.h:424
size_t capacity
Definition nob.h:427
size_t count
Definition nob.h:426
const char ** items
Definition nob.h:425
Definition nob.h:268
size_t capacity
Definition nob.h:271
const char ** items
Definition nob.h:269
size_t count
Definition nob.h:270
Definition nob.h:408
size_t count
Definition nob.h:410
Nob_Proc * items
Definition nob.h:409
size_t capacity
Definition nob.h:411
Definition nob.h:364
size_t count
Definition nob.h:366
char * items
Definition nob.h:365
size_t capacity
Definition nob.h:367
Definition nob.h:628
size_t count
Definition nob.h:629
const char * data
Definition nob.h:630