/nobber/trunk

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