1- /* nob - v1.23 .0 - Public Domain - https://github.com/tsoding/nob.h
1+ /* nob - v1.25 .0 - Public Domain - https://github.com/tsoding/nob.h
22
33 This library is the next generation of the [NoBuild](https://github.com/tsoding/nobuild) idea.
44
8080 - NOB_EXPERIMENTAL_DELETE_OLD - Experimental feature that automatically removes `nob.old` files. It's unclear how well
8181 it works on Windows, so it's experimental for now.
8282 - NOB_STRIP_PREFIX - string the `nob_` prefixes from non-redefinable names.
83+ - NOB_NO_ECHO - do not echo the actions various nob functions are doing (like nob_cmd_run(), nob_mkdir_if_not_exists(), etc).
8384
8485 ## Redefinable Macros
8586
@@ -322,8 +323,21 @@ typedef struct {
322323 size_t capacity ;
323324} Nob_String_Builder ;
324325
326+ #define nob_swap (T , a , b ) do { T t = a; a = b; b = t; } while (0)
327+
325328NOBDEF bool nob_read_entire_file (const char * path , Nob_String_Builder * sb );
326329NOBDEF int nob_sb_appendf (Nob_String_Builder * sb , const char * fmt , ...) NOB_PRINTF_FORMAT (2 , 3 );
330+ // Pads the String_Builder (sb) to the desired word size boundary with 0s.
331+ // Imagine we have sb that contains 5 `a`-s:
332+ //
333+ // aaaa|a
334+ //
335+ // If we pad align it by size 4 it will look like this:
336+ //
337+ // aaaa|a000| <- padded with 0s to the next size 4 boundary
338+ //
339+ // Useful when you are building some sort of binary format using String_Builder.
340+ NOBDEF void nob_sb_pad_align (Nob_String_Builder * sb , size_t size );
327341
328342// Append a sized buffer to a string builder
329343#define nob_sb_append_buf (sb , buf , size ) nob_da_append_many(sb, buf, size)
@@ -521,6 +535,7 @@ NOBDEF bool nob_cmd_run_sync_redirect_and_reset(Nob_Cmd *cmd, Nob_Cmd_Redirect r
521535#define NOB_TEMP_CAPACITY (8*1024*1024)
522536#endif // NOB_TEMP_CAPACITY
523537NOBDEF char * nob_temp_strdup (const char * cstr );
538+ NOBDEF char * nob_temp_strndup (const char * cstr , size_t size );
524539NOBDEF void * nob_temp_alloc (size_t size );
525540NOBDEF char * nob_temp_sprintf (const char * format , ...) NOB_PRINTF_FORMAT (1 , 2 );
526541// nob_temp_reset() - Resets the entire temporary storage to 0.
@@ -557,6 +572,11 @@ NOBDEF int nob_needs_rebuild1(const char *output_path, const char *input_path);
557572NOBDEF int nob_file_exists (const char * file_path );
558573NOBDEF const char * nob_get_current_dir_temp (void );
559574NOBDEF bool nob_set_current_dir (const char * path );
575+ // Returns you the directory part of the path allocated on the temporary storage.
576+ NOBDEF char * nob_temp_dir_name (const char * path );
577+ NOBDEF char * nob_temp_file_name (const char * path );
578+ NOBDEF char * nob_temp_file_ext (const char * path );
579+ NOBDEF char * nob_temp_running_executable_path (void );
560580
561581// TODO: we should probably document somewhere all the compiler we support
562582
@@ -879,20 +899,26 @@ NOBDEF bool nob_mkdir_if_not_exists(const char *path)
879899#endif
880900 if (result < 0 ) {
881901 if (errno == EEXIST ) {
902+ #ifndef NOB_NO_ECHO
882903 nob_log (NOB_INFO , "directory `%s` already exists" , path );
904+ #endif // NOB_NO_ECHO
883905 return true;
884906 }
885907 nob_log (NOB_ERROR , "could not create directory `%s`: %s" , path , strerror (errno ));
886908 return false;
887909 }
888910
911+ #ifndef NOB_NO_ECHO
889912 nob_log (NOB_INFO , "created directory `%s`" , path );
913+ #endif // NOB_NO_ECHO
890914 return true;
891915}
892916
893917NOBDEF bool nob_copy_file (const char * src_path , const char * dst_path )
894918{
919+ #ifndef NOB_NO_ECHO
895920 nob_log (NOB_INFO , "copying %s -> %s" , src_path , dst_path );
921+ #endif // NOB_NO_ECHO
896922#ifdef _WIN32
897923 if (!CopyFile (src_path , dst_path , FALSE)) {
898924 nob_log (NOB_ERROR , "Could not copy file: %s" , nob_win32_error_message (GetLastError ()));
@@ -1114,12 +1140,14 @@ static Nob_Proc nob__cmd_start_process(Nob_Cmd cmd, Nob_Fd *fdin, Nob_Fd *fdout,
11141140 return NOB_INVALID_PROC ;
11151141 }
11161142
1143+ #ifndef NOB_NO_ECHO
11171144 Nob_String_Builder sb = {0 };
11181145 nob_cmd_render (cmd , & sb );
11191146 nob_sb_append_null (& sb );
11201147 nob_log (NOB_INFO , "CMD: %s" , sb .items );
11211148 nob_sb_free (sb );
11221149 memset (& sb , 0 , sizeof (sb ));
1150+ #endif // NOB_NO_ECHO
11231151
11241152#ifdef _WIN32
11251153 // https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output
@@ -1628,7 +1656,9 @@ NOBDEF Nob_File_Type nob_get_file_type(const char *path)
16281656
16291657NOBDEF bool nob_delete_file (const char * path )
16301658{
1659+ #ifndef NOB_NO_ECHO
16311660 nob_log (NOB_INFO , "deleting %s" , path );
1661+ #endif // NOB_NO_ECHO
16321662#ifdef _WIN32
16331663 if (!DeleteFileA (path )) {
16341664 nob_log (NOB_ERROR , "Could not delete file %s: %s" , path , nob_win32_error_message (GetLastError ()));
@@ -1718,6 +1748,15 @@ NOBDEF char *nob_temp_strdup(const char *cstr)
17181748 return result ;
17191749}
17201750
1751+ NOBDEF char * nob_temp_strndup (const char * s , size_t n )
1752+ {
1753+ char * r = nob_temp_alloc (n + 1 );
1754+ NOB_ASSERT (r != NULL && "Extend the size of the temporary allocator" );
1755+ memcpy (r , s , n );
1756+ r [n ] = '\0' ;
1757+ return r ;
1758+ }
1759+
17211760NOBDEF void * nob_temp_alloc (size_t requested_size )
17221761{
17231762 size_t word_size = sizeof (uintptr_t );
@@ -1763,11 +1802,7 @@ NOBDEF void nob_temp_rewind(size_t checkpoint)
17631802
17641803NOBDEF const char * nob_temp_sv_to_cstr (Nob_String_View sv )
17651804{
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 ;
1805+ return nob_temp_strndup (sv .data , sv .count );
17711806}
17721807
17731808NOBDEF int nob_needs_rebuild (const char * output_path , const char * * input_paths , size_t input_paths_count )
@@ -1858,7 +1893,9 @@ NOBDEF const char *nob_path_name(const char *path)
18581893
18591894NOBDEF bool nob_rename (const char * old_path , const char * new_path )
18601895{
1896+ #ifndef NOB_NO_ECHO
18611897 nob_log (NOB_INFO , "renaming %s -> %s" , old_path , new_path );
1898+ #endif // NOB_NO_ECHO
18621899#ifdef _WIN32
18631900 if (!MoveFileEx (old_path , new_path , MOVEFILE_REPLACE_EXISTING )) {
18641901 nob_log (NOB_ERROR , "could not rename %s to %s: %s" , old_path , new_path , nob_win32_error_message (GetLastError ()));
@@ -1932,6 +1969,15 @@ NOBDEF int nob_sb_appendf(Nob_String_Builder *sb, const char *fmt, ...)
19321969 return n ;
19331970}
19341971
1972+ NOBDEF void nob_sb_pad_align (Nob_String_Builder * sb , size_t size )
1973+ {
1974+ size_t rem = sb -> count %size ;
1975+ if (rem == 0 ) return ;
1976+ for (size_t i = 0 ; i < size - rem ; ++ i ) {
1977+ nob_da_append (sb , 0 );
1978+ }
1979+ }
1980+
19351981NOBDEF Nob_String_View nob_sv_chop_by_delim (Nob_String_View * sv , char delim )
19361982{
19371983 size_t i = 0 ;
@@ -2100,6 +2146,89 @@ NOBDEF bool nob_set_current_dir(const char *path)
21002146#endif // _WIN32
21012147}
21022148
2149+ NOBDEF char * nob_temp_dir_name (const char * path )
2150+ {
2151+ #ifndef _WIN32
2152+ // Stolen from the musl's implementation of dirname.
2153+ // We are implementing our own one because libc vendors cannot agree on whether dirname(3)
2154+ // modifies the path or not.
2155+ if (!path || !* path ) return "." ;
2156+ size_t i = strlen (path ) - 1 ;
2157+ for (; path [i ] == '/' ; i -- ) if (!i ) return "/" ;
2158+ for (; path [i ] != '/' ; i -- ) if (!i ) return "." ;
2159+ for (; path [i ] == '/' ; i -- ) if (!i ) return "/" ;
2160+ return nob_temp_strndup (path , i + 1 );
2161+ #else
2162+ if (!path ) path = "" ; // Treating NULL as empty.
2163+ char * drive = nob_temp_alloc (_MAX_DRIVE );
2164+ char * dir = nob_temp_alloc (_MAX_DIR );
2165+ // https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/8e46eyt7(v=vs.100)
2166+ errno_t ret = _splitpath_s (path , drive , _MAX_DRIVE , dir , _MAX_DIR , NULL , 0 , NULL , 0 );
2167+ NOB_ASSERT (ret == 0 );
2168+ return nob_temp_sprintf ("%s%s" , drive , dir );
2169+ #endif // _WIN32
2170+ }
2171+
2172+ NOBDEF char * nob_temp_file_name (const char * path )
2173+ {
2174+ #ifndef _WIN32
2175+ // Stolen from the musl's implementation of dirname.
2176+ // We are implementing our own one because libc vendors cannot agree on whether basename(3)
2177+ // modifies the path or not.
2178+ if (!path || !* path ) return "." ;
2179+ char * s = nob_temp_strdup (path );
2180+ size_t i = strlen (s )- 1 ;
2181+ for (; i && s [i ]== '/' ; i -- ) s [i ] = 0 ;
2182+ for (; i && s [i - 1 ]!= '/' ; i -- );
2183+ return s + i ;
2184+ #else
2185+ if (!path ) path = "" ; // Treating NULL as empty.
2186+ char * fname = nob_temp_alloc (_MAX_FNAME );
2187+ char * ext = nob_temp_alloc (_MAX_EXT );
2188+ // https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/8e46eyt7(v=vs.100)
2189+ errno_t ret = _splitpath_s (path , NULL , 0 , NULL , 0 , fname , _MAX_FNAME , ext , _MAX_EXT );
2190+ NOB_ASSERT (ret == 0 );
2191+ return nob_temp_sprintf ("%s%s" , fname , ext );
2192+ #endif // _WIN32
2193+ }
2194+
2195+ NOBDEF char * nob_temp_file_ext (const char * path )
2196+ {
2197+ #ifndef _WIN32
2198+ return strrchr (nob_temp_file_name (path ), '.' );
2199+ #else
2200+ if (!path ) path = "" ; // Treating NULL as empty.
2201+ char * ext = nob_temp_alloc (_MAX_EXT );
2202+ // https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/8e46eyt7(v=vs.100)
2203+ errno_t ret = _splitpath_s (path , NULL , 0 , NULL , 0 , NULL , 0 , ext , _MAX_EXT );
2204+ NOB_ASSERT (ret == 0 );
2205+ return ext ;
2206+ #endif // _WIN32
2207+ }
2208+
2209+ NOBDEF char * nob_temp_running_executable_path (void )
2210+ {
2211+ #if defined(__linux__ )
2212+ char buf [4096 ];
2213+ int length = readlink ("/proc/self/exe" , buf , NOB_ARRAY_LEN (buf ));
2214+ if (length < 0 ) return "" ;
2215+ return nob_temp_strndup (buf , length );
2216+ #elif defined(_WIN32 )
2217+ char buf [MAX_PATH ];
2218+ int length = GetModuleFileNameA (NULL , buf , MAX_PATH );
2219+ return nob_temp_strndup (buf , length );
2220+ #elif defined(__APPLE__ )
2221+ char buf [4096 ];
2222+ uint32_t size = NOB_ARRAY_LEN (buf );
2223+ if (_NSGetExecutablePath (buf , & size ) != 0 ) return "" ;
2224+ int length = strlen (buf );
2225+ return nob_temp_strndup (buf , length );
2226+ #else
2227+ fprintf (stderr , "%s:%d: TODO: nob_temp_running_executable_path is not implemented for this platform\n" , __FILE__ , __LINE__ );
2228+ return "" ;
2229+ #endif
2230+ }
2231+
21032232// minirent.h SOURCE BEGIN ////////////////////////////////////////
21042233#if defined(_WIN32 ) && !defined(NOB_NO_MINIRENT )
21052234struct DIR
@@ -2236,12 +2365,14 @@ NOBDEF int closedir(DIR *dirp)
22362365 #define da_last nob_da_last
22372366 #define da_remove_unordered nob_da_remove_unordered
22382367 #define da_foreach nob_da_foreach
2368+ #define swap nob_swap
22392369 #define String_Builder Nob_String_Builder
22402370 #define read_entire_file nob_read_entire_file
22412371 #define sb_appendf nob_sb_appendf
22422372 #define sb_append_buf nob_sb_append_buf
22432373 #define sb_append_cstr nob_sb_append_cstr
22442374 #define sb_append_null nob_sb_append_null
2375+ #define sb_pad_align nob_sb_pad_align
22452376 #define sb_free nob_sb_free
22462377 #define Proc Nob_Proc
22472378 #define INVALID_PROC NOB_INVALID_PROC
@@ -2274,6 +2405,7 @@ NOBDEF int closedir(DIR *dirp)
22742405 #define cmd_run_sync_redirect nob_cmd_run_sync_redirect
22752406 #define cmd_run_sync_redirect_and_reset nob_cmd_run_sync_redirect_and_reset
22762407 #define temp_strdup nob_temp_strdup
2408+ #define temp_strndup nob_temp_strndup
22772409 #define temp_alloc nob_temp_alloc
22782410 #define temp_sprintf nob_temp_sprintf
22792411 #define temp_reset nob_temp_reset
@@ -2287,6 +2419,10 @@ NOBDEF int closedir(DIR *dirp)
22872419 #define file_exists nob_file_exists
22882420 #define get_current_dir_temp nob_get_current_dir_temp
22892421 #define set_current_dir nob_set_current_dir
2422+ #define temp_dir_name nob_temp_dir_name
2423+ #define temp_file_name nob_temp_file_name
2424+ #define temp_file_ext nob_temp_file_ext
2425+ #define temp_running_executable_path nob_temp_running_executable_path
22902426 #define String_View Nob_String_View
22912427 #define temp_sv_to_cstr nob_temp_sv_to_cstr
22922428 #define sv_chop_by_delim nob_sv_chop_by_delim
@@ -2310,6 +2446,14 @@ NOBDEF int closedir(DIR *dirp)
23102446/*
23112447 Revision history:
23122448
2449+ 1.25.0 (2025-10-25) - Add nob_sb_pad_align()
2450+ - Add nob_swap()
2451+ - Add nob_temp_strndup()
2452+ - Add nob_temp_dir_name()
2453+ - Add nob_temp_file_name()
2454+ - Add nob_temp_file_ext()
2455+ - Add nob_temp_running_executable_path()
2456+ 1.24.0 (2025-10-23) Introduce NOB_NO_ECHO macro flag (@rexim)
23132457 1.23.0 (2025-08-22) Introduce new API for running commands (by @rexim, @programmerlexi, @0x152a)
23142458 - Add nob_cmd_run()
23152459 - Add nob_cmd_run_opt()
0 commit comments