Compare commits

...

13 Commits

Author SHA1 Message Date
0x221E
3b6873ebad Patch: Fix off-by-one at command.c command_create_f_to_o() 2026-01-26 18:51:04 +01:00
0x221E
d68fc7db47 Readme: Add link to universe.0xinfinity.dev 2026-01-19 00:37:45 +01:00
0x221E
87085661a2 Fix: Backend depends on b->cap instead of MEM_BACKEND_MAX_CAP in backend_reserve()
Style: Remove redundant comments in main.c
2026-01-19 00:18:50 +01:00
0x221E
c7a96b14e8 Refactor: command_create_f_to_o() and command_get_profile() 2026-01-18 22:39:32 +01:00
0x221E
3cba603e7c Fix: Build phase for C. 2026-01-18 18:24:00 +01:00
0x221E
abe970c173 Feature: Add capability to build multiple files. 2026-01-18 16:43:23 +01:00
0x221E
5fd1f72624 Readme: Modify README.md to fit current version 2026-01-18 11:18:27 +01:00
0x221E
fbb1868d82 Style: Rename string_createcat_cc to string_concat_cc 2026-01-18 10:23:22 +01:00
0x221E
1b889b7625 Feature: Add command execution on child processes feature
Temporary: Add configuration sample for forward compatibility
2026-01-17 18:21:08 +01:00
0x221E
e61af99a0c Patch: Add arena constructor to memory.h 2026-01-17 18:19:40 +01:00
0x221E
38ca8ae719 Feature: Add C compiler discovery and file discovery feature 2026-01-17 18:18:26 +01:00
0x221E
b62d35a130 Refactor: Remove reduntant functions from string and memory module 2026-01-17 18:17:46 +01:00
0x221E
724b0047e6 Patch: Fix variadic use in die_f and log_f 2026-01-17 18:15:40 +01:00
20 changed files with 429 additions and 45 deletions

View File

@@ -1,5 +1,7 @@
# ibuild # ibuild
A simple build system written in C. The project is incomplete and is not meant to be used by anyone. It is designed to work out-of-the-box on C/C++ projects. However, it has a Recursive Descent Parser and supports a very small DSL. The DSL is in the format of a very basic configuration file. As of now, there is no complex configuration support as well as cross-platform compatibility features, as this is only available in linux. A simple build system written in C. The project is incomplete and is not meant to be used by anyone. It is designed to work out-of-the-box on C/C++ projects.
This project is maintained in: <https://universe.0xinfinity.dev/0x221E/ibuild>, where the plans, issues, and wiki are located.
## Security Disclaimer ## Security Disclaimer
The project has not been battle-tested yet. I am not using as a daily driver yet, and when I do, I will conduct thorough testing by fuzzing the application continously for a period of time (likely a week). The project has not been battle-tested yet. I am not using as a daily driver yet, and when I do, I will conduct thorough testing by fuzzing the application continously for a period of time (likely a week).
@@ -12,13 +14,12 @@ You either place the executable to your /usr/local/bin, or place it into your pr
Then run `ibuild`, and it should build the binary. Then run `ibuild`, and it should build the binary.
## Configuration File ## Configuration File
Below is a sample configuration file; Below is a sample (future) syntax;
```` ````
COMPILER_PATH = "/usr/bin/gcc" KEY = "VALUE";
SRC_DIR = "src" # Recursively discovers files here KEY = ["VALUE1", "VALUE2"];
SRC_FILES = ["src/main.c"] # Manual file specification is optional ````
TARGET_EXEC = "ibuild" # executable name ### All Key-Values
DEP_SEARCHER = "/usr/bin/gcc" # Default, no need to specify explicitly | Key | Value |
DEP_SEARCHER_FLAGS = "-MM" # Default, no need to specify explicitly | --- | --- |
VERSION = "0.0.1" # Can specify version here, which would be passed as a macro. | CC_COMPILER_PATH | `e.g. /usr/bin/gcc`|
````

View File

@@ -1 +1 @@
gcc -o ibuild src/main.c src/memory.c src/utils.c src/string.c src/discovery.c src/io.c -fsanitize=address -g -DDEBUG gcc -o example/ibuild src/main.c src/memory.c src/utils.c src/string.c src/discovery.c src/io.c src/command.c src/build.c -fsanitize=address -g -DDEBUG -Wall -Wextra -Wpedantic -Wshadow

8
example/main.c Normal file
View File

@@ -0,0 +1,8 @@
#include <stdio.h>
int main(void)
{
printf("Hello world!\n");
printf("This is build 2!\n");
return 0;
}

1
example/test.c Normal file
View File

@@ -0,0 +1 @@

87
src/build.c Normal file
View File

@@ -0,0 +1,87 @@
#include "build.h"
#include <stddef.h>
#include <assert.h>
#include "memory.h"
#include "string.h"
#include "discovery.h"
#include "command.h"
#include "io.h"
#include "utils.h"
// Only supports C and multiple files
void build(BuildContext* bc)
{
assert(bc != NULL);
assert(bc->sp != NULL);
int sc = build_c_to_o(bc);
if(sc != 0) DIE("Compilation failed!");
link(bc);
}
void link(BuildContext* bc)
{
assert(bc != NULL);
if(bc->obj_files == NULL) DIE("Linking process was triggered, however there are no object files.");
Command cmd;
cmd.a = bc->a;
cmd.app = bc->d->cc;
size_t len = bc->d->c_count + 2;
const char** args = (const char**)arena_alloc(bc->a, sizeof(const char*) * len);
args[0] = bc->d->cc.buf;
for(size_t i = 0; i < bc->d->c_count; i++)
{
args[i + 1] = bc->obj_files[i]->buf;
}
args[len - 1] = NULL;
cmd.args = args;
int status = command_run(&cmd);
if(status != 0)
DIE("Linking failed with status code: %d");
LOG_DEBUG("Linking completed!");
}
int build_c_to_o(BuildContext* bc)
{
assert(bc != NULL);
if(bc->d->c_count == 0)
return 1;
CommandOptions co;
co.a = bc->a;
co.sp = bc->sp;
co.bp = bc->build_profile;
co.c = bc->c;
co.app = bc->d->cc;
StringView* arr = (StringView*)arena_alloc(bc->a, sizeof(StringView) * 1);
co.files = arr;
StringView** obj_files = (StringView**)arena_alloc(bc->a, sizeof(StringView*) * bc->d->c_count);
bc->obj_files = obj_files;
for(size_t i = 0; i < bc->d->c_count; i++)
{
co.files[0] = bc->d->c_files[i]->path;
Command cmd = command_create_f_to_o(&co);
int status = command_run(&cmd);
if(status != 0)
DIE("Failed to compile %s, compiler exited with status code: %d", bc->d->c_files[i]->path.buf, status);
bc->obj_files[i] = &bc->d->c_files[i]->path;
LOG_DEBUG("Compiling %s", bc->d->c_files[i]->path.buf);
}
return 0;
}

36
src/build.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef BUILD_H
#define BUILD_H
#include "common.h"
typedef struct Configuration Configuration;
typedef struct Discovery Discovery;
typedef struct Arena Arena;
typedef struct StringPool StringPool;
typedef struct StringView StringView;
typedef struct Command Command;
struct BuildContext
{
// Memory
Arena* a;
StringPool* sp;
// Build Params
enum BuildProfile build_profile;
Configuration* c;
Discovery* d;
StringView** obj_files;
};
typedef enum BuildProfile BuildProfile;
typedef struct BuildContext BuildContext;
void build(BuildContext* bc);
void link(BuildContext* bc);
int build_c_to_o(BuildContext* bc);
#endif

87
src/command.c Normal file
View File

@@ -0,0 +1,87 @@
#include "command.h"
#include "memory.h"
#include "discovery.h"
#include "io.h"
#include "utils.h"
#include "config.h"
#include <assert.h>
#include <unistd.h>
#include <sys/wait.h>
// TODO: This file is UNSAFE. This file is purely here for MVP purposes, and to self-host this program itself.
Command command_create_f_to_o(CommandOptions* co)
{
assert(co != NULL);
size_t len_args = 6;
ProfileResult pr = command_get_profile(co);
len_args += pr.compiler_flags_count;
const char** args_buf = (const char**)arena_alloc(co->a, sizeof(const char*) * len_args);
args_buf[0] = co->app.buf;
args_buf[1] = "-c";
args_buf[2] = co->files[0].buf;
args_buf[3] = "-o";
StringView o_ext = STR_LIT(co->sp, ".o");
StringView f_o = string_concat_ss(co->sp, &co->files[0], &o_ext);
args_buf[4] = f_o.buf;
args_buf[5] = "-MMD";
for (size_t i = 0; i < pr.compiler_flags_count; i++)
{
args_buf[i + 6] = pr.compiler_flags_buf[i].buf;
}
args_buf[len_args] = NULL;
LOG_DEBUG("Command created, first 4 args: %s %s %s %s %s | total args: %d", args_buf[0], args_buf[1], args_buf[2], args_buf[3], args_buf[4], len_args);
return (Command){.a = co->a, .app = co->app, .args = args_buf};
}
ProfileResult command_get_profile(CommandOptions* co)
{
ProfileResult pr;
switch(co->bp)
{
case B_DEBUG:
pr.compiler_flags_count = co->c->debug_compiler_flags_c;
pr.compiler_flags_buf = co->c->debug_compiler_flags;
break;
case B_PROD:
pr.compiler_flags_count = co->c->prod_compiler_flags_c;
pr.compiler_flags_buf = co->c->prod_compiler_flags;
break;
default:
DIE("No configuration was selected!");
}
return pr;
}
// TODO: Add input sanitization to cmd->app and cmd->args
int command_run(Command* cmd)
{
assert(cmd != NULL);
if(cmd->args == NULL) DIE("No arguments were supplied to the command");
pid_t pid = fork();
if(pid == -1) DIE("fork failed, could not create child process.");
if(pid == 0)
{
if(execvp(cmd->app.buf, (char*const*)cmd->args) == -1)
DIE("Failed to launch command: %s.", cmd->app.buf);
}
int wstatus = 0;
if(waitpid(pid, &wstatus, 0) == -1) DIE("wait failed.");
LOG_DEBUG("Command calling app %s exited with status code %d on child process PID %d.", cmd->app.buf, wstatus, pid);
return wstatus;
}

44
src/command.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef COMMAND_H
#define COMMAND_H
#include "string.h"
#include "common.h"
typedef struct Arena Arena;
typedef struct Discovery Discovery;
typedef struct Configuration Configuration;
struct CommandOptions
{
Arena* a;
StringPool* sp;
BuildProfile bp;
Configuration* c;
StringView app;
StringView* files;
};
struct ProfileResult
{
size_t compiler_flags_count;
StringView* compiler_flags_buf;
};
struct Command
{
Arena* a;
StringView app;
const char** args;
};
typedef struct Argument Argument;
typedef struct Command Command;
typedef struct CommandOptions CommandOptions;
typedef struct ProfileResult ProfileResult;
Command command_create_f_to_o(CommandOptions* co);
ProfileResult command_get_profile(CommandOptions* co);
int command_run(Command* cmd);
#endif

12
src/common.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef COMMON_H
#define COMMON_H
enum BuildProfile
{
B_DEBUG,
B_PROD
};
typedef enum BuildProfile BuildProfile;
#endif

2
src/config.c Normal file
View File

@@ -0,0 +1,2 @@
#include "config.h"

30
src/config.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef CONFIG_H
#define CONFIG_H
#include "string.h"
struct Configuration
{
// C-Compiler Flags
StringView cc_compiler_path;
StringView src_dir;
// General Flags
//StringView* src_files;
// Debug Profile
StringView* debug_compiler_flags;
size_t debug_compiler_flags_c;
// Production Profile
StringView* prod_compiler_flags;
size_t prod_compiler_flags_c;
// Linker arguments
StringView* linker_flags;
size_t linker_flags_c;
};
typedef struct Configuration Configuration;
#endif

View File

@@ -7,6 +7,9 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
Discovery discovery_create(Arena* a, StringPool* sp) Discovery discovery_create(Arena* a, StringPool* sp)
{ {
@@ -31,23 +34,18 @@ Discovery discovery_create(Arena* a, StringPool* sp)
return d; return d;
} }
void discovery_cleanup(Discovery* d)
{
}
void discovery_discover(Discovery* d) void discovery_discover(Discovery* d)
{ {
assert(d != NULL); assert(d != NULL);
DirOpContext doc; DirOpContext doc;
doc.a_f = d->a; doc.a_f = d->a;
doc.sp = d->sp; doc.sp = d->sp;
StringView path = SV(d->sp, "."); StringView path = STR_LIT(d->sp, ".");
size_t count = dir_get_recursive(&doc, &path); size_t count = dir_get_recursive(&doc, &path);
d->file_count = count; d->file_count = count;
LOG_DEBUG("Discovered %d files.", count); LOG_DEBUG("Discovered %d files.", count);
StringView c_ext = SV(d->sp, ".c"); StringView c_ext = STR_LIT(d->sp, ".c");
ExtDiscoveryResult c_res = discovery_discover_ext(d, d->a, &c_ext); ExtDiscoveryResult c_res = discovery_discover_ext(d, d->a, &c_ext);
if(c_res.mem != NULL) if(c_res.mem != NULL)
@@ -57,7 +55,7 @@ void discovery_discover(Discovery* d)
d->c_files = c_res.mem; d->c_files = c_res.mem;
} }
StringView h_ext = SV(d->sp, ".h"); StringView h_ext = STR_LIT(d->sp, ".h");
ExtDiscoveryResult h_res = discovery_discover_ext(d, d->a, &h_ext); ExtDiscoveryResult h_res = discovery_discover_ext(d, d->a, &h_ext);
if(h_res.mem != NULL) if(h_res.mem != NULL)
@@ -66,6 +64,35 @@ void discovery_discover(Discovery* d)
d->h_count = h_res.len; d->h_count = h_res.len;
d->h_files = h_res.mem; d->h_files = h_res.mem;
} }
if(d->c_count > 0)
{
discovery_discover_ccompiler(d);
}
}
void discovery_discover_ccompiler(Discovery* d)
{
assert(d != NULL);
StringView gcc = STR_LIT(d->sp, "gcc");
StringView cc_gcc = discovery_discover_path(d, &gcc);
if(cc_gcc.buf != NULL)
{
d->cc = cc_gcc;
return;
}
StringView clang = STR_LIT(d->sp, "clang");
StringView cc_clang = discovery_discover_path(d, &clang);
if(cc_clang.buf != NULL)
{
d->cc = cc_clang;
return;
}
LOG_DEBUG("No C compilers were found, however, %d C files were found. Not installed?", d->c_count);
} }
// TODO: Make the search logic faster, or at least search all of the wanted extension at once, uniformly. // TODO: Make the search logic faster, or at least search all of the wanted extension at once, uniformly.
@@ -91,9 +118,40 @@ ExtDiscoveryResult discovery_discover_ext(Discovery* d, Arena* a, StringView* ex
DirEntry** entry = (DirEntry**)arena_alloc(a, sizeof(DirEntry*)); DirEntry** entry = (DirEntry**)arena_alloc(a, sizeof(DirEntry*));
*entry = &d->files[i]; *entry = &d->files[i];
count++; count++;
continue;
} }
} }
if(count > 0) return (ExtDiscoveryResult){.mem = res, .len = count}; if(count > 0) return (ExtDiscoveryResult){.mem = res, .len = count};
return (ExtDiscoveryResult){.mem = NULL, .len = 0}; return (ExtDiscoveryResult){.mem = NULL, .len = 0};
} }
StringView discovery_discover_path(Discovery* d, StringView* s)
{
assert(d != NULL);
assert(s != NULL);
char* path_env = getenv("PATH");
if(path_env == NULL) return (StringView){.buf = NULL, .len = 0};
char* path = strdup(path_env);
errno = 0;
if(path == NULL && errno != 0) DIE("String copy failure.");
char* current = strtok(path, ":");
do
{
LOG_DEBUG("Checking: %s for %s in PATH.", current, s->buf);
StringView p = string_create(d->sp, current, strlen(current));
StringView fp = path_concat_ss(d->sp, &p, s);
if(access(fp.buf, X_OK) == 0)
{
LOG_DEBUG("Found %s in PATH:%s", s->buf, p.buf);
return fp;
}
}while((current = strtok(NULL, ":")) != NULL);
return (StringView){.buf = NULL, .len = 0};
}

View File

@@ -3,11 +3,12 @@
typedef struct Arena Arena; typedef struct Arena Arena;
typedef struct StringPool StringPool; typedef struct StringPool StringPool;
typedef struct StringView StringView;
typedef struct DirEntry DirEntry; typedef struct DirEntry DirEntry;
#include <stddef.h> #include <stddef.h>
#include "string.h"
struct Discovery struct Discovery
{ {
// Memory // Memory
@@ -25,6 +26,9 @@ struct Discovery
size_t c_count; size_t c_count;
size_t h_count; size_t h_count;
size_t cpp_count; size_t cpp_count;
StringView cc;
StringView cxx;
}; };
struct ExtDiscoveryResult struct ExtDiscoveryResult
@@ -40,6 +44,9 @@ Discovery discovery_create(Arena* a, StringPool* sp);
void discovery_cleanup(Discovery* d); void discovery_cleanup(Discovery* d);
void discovery_discover(Discovery* d); void discovery_discover(Discovery* d);
void discovery_discover_ccompiler(Discovery* d);
ExtDiscoveryResult discovery_discover_ext(Discovery* d, Arena* a, StringView* ext); ExtDiscoveryResult discovery_discover_ext(Discovery* d, Arena* a, StringView* ext);
StringView discovery_discover_path(Discovery* d, StringView* s);
#endif #endif

View File

@@ -14,10 +14,10 @@ StringView path_concat_ss(StringPool *sp, StringView* a, StringView* b)
if(b == NULL) DIE("b must be a valid string!"); if(b == NULL) DIE("b must be a valid string!");
if(a->len <= 0 || b->len <= 0) DIE("a and b must not be empty! (a:%d, b:%d)", a->len, b->len); if(a->len <= 0 || b->len <= 0) DIE("a and b must not be empty! (a:%d, b:%d)", a->len, b->len);
if(a->buf[a->len - 1] == '/') if(a->buf[a->len - 1] == '/')
return string_createcat_ss(sp, a, b); return string_concat_ss(sp, a, b);
StringView slash = (StringView){.buf = "/", .len = 1}; StringView slash = (StringView){.buf = "/", .len = 1};
StringView ts = string_createcat_ss(sp, a, &slash); StringView ts = string_concat_ss(sp, a, &slash);
return string_createcat_ss(sp, &ts, b); return string_concat_ss(sp, &ts, b);
} }
// Utilize DFS for recursive directory search. // Utilize DFS for recursive directory search.
@@ -40,7 +40,7 @@ size_t dir_get_recursive(DirOpContext* doc, StringView* p)
if(de == NULL && errno != 0) DIE("failed to read directory: \"%s\"", p); if(de == NULL && errno != 0) DIE("failed to read directory: \"%s\"", p);
if(de == NULL) break; if(de == NULL) break;
if(de->d_type == DT_UNKNOWN) DIE("Filesystem not supported!!"); if(de->d_type == DT_UNKNOWN) DIE("Filesystem not supported!!"); // Add support for modern filesystems using stat()
if(de->d_type != DT_DIR && de->d_type != DT_REG) continue; if(de->d_type != DT_DIR && de->d_type != DT_REG) continue;
if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0 || strcmp(de->d_name, ".git") == 0) continue; if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0 || strcmp(de->d_name, ".git") == 0) continue;

View File

@@ -7,20 +7,34 @@
#include "utils.h" #include "utils.h"
#include "string.h" #include "string.h"
#include "discovery.h" #include "discovery.h"
#include "command.h"
#include "config.h"
#include "build.h"
int main(void) { int main(void)
Backend b = {NULL, 0}; {
Backend b = BACKEND_CONST;
backend_initialize(&b); backend_initialize(&b);
Arena a_sp = arena_create(&b, MEM_ARENA_MAX_CAP); Arena a_sp = arena_create(&b, MEM_ARENA_MAX_CAP);
StringPool sp = string_pool_create(&a_sp); StringPool sp = string_pool_create(&a_sp);
Arena a_sptemp = arena_create(&b, MEM_ARENA_MAX_CAP);
StringPool sp_temp = string_pool_create(&a_sptemp);
Arena a_d = arena_create(&b, 1024*1024*5); // 5MB for Discovery Arena a_d = arena_create(&b, 1024*1024*5); // 5MB for Discovery
Discovery d = discovery_create(&a_d, &sp); Discovery d = discovery_create(&a_d, &sp);
discovery_discover(&d); discovery_discover(&d);
Configuration sample_config;
Arena a_b = arena_create(&b, 1024*1024*5);
BuildContext bc;
bc.build_profile = B_DEBUG;
bc.d = &d;
bc.c = &sample_config;
bc.a = &a_b;
bc.sp = &sp;
build(&bc);
return 0; return 0;
} }

View File

@@ -27,7 +27,7 @@ void* backend_reserve(Backend* b, size_t cap)
{ {
assert(b != NULL); assert(b != NULL);
void* ptr = b->last; void* ptr = b->last;
if(b->len + cap >= MEM_BACKEND_MAX_CAP) if(b->len + cap >= b->cap)
DIE("Process out of memory, requested %d (current usage %d out of %d).", cap, b->len, b->cap); DIE("Process out of memory, requested %d (current usage %d out of %d).", cap, b->len, b->cap);
b->last = (char*)b->last + (int)cap; b->last = (char*)b->last + (int)cap;
b->len += cap; b->len += cap;

View File

@@ -1,7 +1,6 @@
#ifndef MEMORY_H #ifndef MEMORY_H
#define MEMORY_H #define MEMORY_H
#ifndef MEM_ARENA_MAX_CAP #ifndef MEM_ARENA_MAX_CAP
#define MEM_ARENA_MAX_CAP 1024 * 1024 #define MEM_ARENA_MAX_CAP 1024 * 1024
#endif #endif
@@ -20,16 +19,20 @@ struct Backend
size_t cap; size_t cap;
}; };
#define BACKEND_CONST {NULL, NULL, 0, 0}
struct Arena struct Arena
{ {
void* mem; void* mem;
void* last; void* last;
size_t len; size_t len;
size_t cap; size_t cap;
void* next; void* next; //TODO
struct Backend* b; struct Backend* b;
}; };
#define ARENA_CONST {NULL, NULL, 0, 0, NULL}
typedef struct Backend Backend; typedef struct Backend Backend;
typedef struct Arena Arena; typedef struct Arena Arena;

View File

@@ -14,11 +14,6 @@ StringPool string_pool_create(Arena* a)
return sp; return sp;
} }
void string_pool_reset(StringPool* ps)
{
// arena_reset(&ps->a);
}
StringView string_create(StringPool* sp, const char* s, size_t len) StringView string_create(StringPool* sp, const char* s, size_t len)
{ {
assert(sp != NULL); assert(sp != NULL);
@@ -30,7 +25,7 @@ StringView string_create(StringPool* sp, const char* s, size_t len)
return (StringView){.buf = buf, .len=len}; return (StringView){.buf = buf, .len=len};
} }
StringView string_createcat_ss(StringPool* sp, StringView* a, StringView* b) StringView string_concat_ss(StringPool* sp, StringView* a, StringView* b)
{ {
if(a == NULL) DIE("a must be a valid C-style string!"); if(a == NULL) DIE("a must be a valid C-style string!");
if(b == NULL) DIE("b must be a valid C-style string!"); if(b == NULL) DIE("b must be a valid C-style string!");

View File

@@ -5,7 +5,7 @@
#include <stddef.h> #include <stddef.h>
#define SV(sp, s) string_create(sp, s, sizeof(s) - 1) #define STR_LIT(sp, s) string_create(sp, s, sizeof(s) - 1)
struct StringPool struct StringPool
{ {
@@ -22,11 +22,10 @@ typedef struct StringPool StringPool;
typedef struct StringView StringView; typedef struct StringView StringView;
StringPool string_pool_create(Arena* a); StringPool string_pool_create(Arena* a);
void string_pool_reset(StringPool* sp);
StringView string_create(StringPool* sp, const char* s, size_t len); StringView string_create(StringPool* sp, const char* s, size_t len);
// Ensure that lengths do not contain null term // Ensure that lengths do not contain null term
StringView string_createcat_ss(StringPool* sp, StringView* a, StringView* b); StringView string_concat_ss(StringPool* sp, StringView* a, StringView* b);
#endif #endif

View File

@@ -10,10 +10,10 @@
void die_f(const char* func, int l, const char* fmt,...) void die_f(const char* func, int l, const char* fmt,...)
{ {
va_list args; va_list args;
va_start(args); va_start(args, fmt);
printf("IBUILD Error (%d:%s): ", l, func); printf("IBUILD Error (%d:%s): ", l, func);
vprintf(fmt, args); vprintf(fmt, args);
printf("\nlast syscall error: \n", strerror(errno)); printf("\nlast syscall error: %s \n", strerror(errno));
va_end(args); va_end(args);
exit(1); exit(1);
} }
@@ -21,7 +21,7 @@ void die_f(const char* func, int l, const char* fmt,...)
void log_f(const char* func, int l, const char* fmt, ...) void log_f(const char* func, int l, const char* fmt, ...)
{ {
va_list args; va_list args;
va_start(args); va_start(args, fmt);
printf("IBUILD Log (%d:%s): ", l, func); printf("IBUILD Log (%d:%s): ", l, func);
vprintf(fmt, args); vprintf(fmt, args);
printf("\n"); printf("\n");