Add: Simple recursive file discovery feature and organization of C/Cpp/H files

This commit is contained in:
0x221E
2026-01-14 23:56:35 +01:00
parent 21983b7da6
commit bb5f514d01
2 changed files with 213 additions and 36 deletions

2
deps/utils vendored

Submodule deps/utils updated: e5cf1a23b3...07c0bfe478

235
ibuild.c
View File

@@ -1,3 +1,11 @@
#define CONFIG_FILE "IBUILD"
#define MAX_STATEMENTS 50
#define MAX_TOKENS 200
#define MAX_FILES_IN_DIR 100
#define MAX_C_FILES 200
#define MAX_H_FILES 200
#define MAX_CPP_FILES 200
#include "deps/utils/src/arena_alloc.h" #include "deps/utils/src/arena_alloc.h"
#include <stdio.h> #include <stdio.h>
@@ -7,10 +15,8 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <stdarg.h> #include <stdarg.h>
#include <assert.h>
#define CONFIG_FILE "IBUILD" #include <dirent.h>
#define MAX_STATEMENTS 50
#define MAX_TOKENS 200
#define DIE(fmt, ...) die_t(__func__, __LINE__, fmt, ##__VA_ARGS__) #define DIE(fmt, ...) die_t(__func__, __LINE__, fmt, ##__VA_ARGS__)
#define LOG_ERROR(fmt, ...) log_write(__func__, fmt, ## __VA_ARGS__) #define LOG_ERROR(fmt, ...) log_write(__func__, fmt, ## __VA_ARGS__)
@@ -29,6 +35,7 @@ typedef struct
Arena configuration; Arena configuration;
Arena strings; Arena strings;
Arena parser; Arena parser;
Arena discovery;
} Memory; } Memory;
static Memory memory; static Memory memory;
@@ -39,6 +46,7 @@ void memory_free()
arena_free(&memory.configuration); arena_free(&memory.configuration);
arena_free(&memory.strings); arena_free(&memory.strings);
arena_free(&memory.parser); arena_free(&memory.parser);
arena_free(&memory.discovery);
} }
/*** logging ***/ /*** logging ***/
@@ -47,12 +55,15 @@ void memory_free()
// Include this log in your issues // Include this log in your issues
void die_t(const char* func, int line, const char* fmt, ...) void die_t(const char* func, int line, const char* fmt, ...)
{ {
assert(func != NULL);
assert(fmt != NULL);
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
printf("ibuild exception(f:%s-l:%d): ", func, line); printf("ibuild exception(f:%s-l:%d): ", func, line);
vprintf(fmt, args); vprintf(fmt, args);
printf("\n"); printf("\n");
printf("******Last syscall error: %s\n", strerror(errno)); printf("******Last syscall error: %s\n", strerror(errno));
va_end(args);
exit(1); exit(1);
} }
@@ -72,6 +83,72 @@ void log_write(const char* func, const char* fmt, ...)
/*** file management ***/ /*** file management ***/
typedef struct
{
const char* path;
unsigned char type;
} DirEntry;
int dirent_get_next(DIR *d, DirEntry *e, const char *p)
{
assert(d != NULL);
struct dirent* file = readdir(d);
if(file == NULL)
return 1;
switch(file->d_type)
{
case DT_DIR:
e->type = DT_DIR;
break;
case DT_REG:
e->type = DT_REG;
break;
default:
return 1;
}
size_t fs = strlen(file->d_name);
char* buf = (char*)arena_alloc(&memory.strings, fs + 1);
memcpy(buf, file->d_name, fs + 1);
buf[fs] = '\0';
e->path = buf;
return 0;
}
size_t dirent_get_recursive(const char* p)
{
if(p == NULL) DIE("p must not be NULL");
DIR* dir = opendir(p);
DirEntry current;
size_t count = 0;
while(dirent_get_next(dir, &current, p) == 0)
{
if(current.type == DT_DIR && (strcmp(current.path, ".") == 0 || strcmp(current.path, "..") == 0 || strcmp(current.path, ".git") == 0))
continue;
DirEntry* ptr = (DirEntry*)arena_alloc(&memory.discovery, sizeof(DirEntry));
size_t size = strlen(current.path) + 2 + strlen(p);
char* buf = (char*)arena_alloc(&memory.strings, size);
snprintf(buf, size, "%s/%s", p, current.path);
current.path = buf;
*ptr = current;
count++;
if(current.type == DT_DIR)
{
count += dirent_get_recursive(buf);
continue;
}
}
closedir(dir);
return count;
}
long file_get_size(FILE* fd) long file_get_size(FILE* fd)
{ {
if(fd == NULL) DIE("fd cannot be NULL."); if(fd == NULL) DIE("fd cannot be NULL.");
@@ -100,6 +177,62 @@ bool file_has_r_access(const char* f)
return false; return false;
} }
/*** discovery phase ***/
typedef struct
{
DirEntry** c_files;
DirEntry** h_files;
DirEntry** cpp_files;
DirEntry** o_files;
} Discovery;
void discover(Discovery *d)
{
size_t amount = dirent_get_recursive(".");
if(amount == 0) DIE("Directory is empty! If you have symlinks in the directory, ibuild does not support them.");
size_t c_c = 0;
size_t h_c = 0;
size_t cpp_c = 0;
for(size_t i = 0; i < amount; i++)
{
char* current = NULL;
if((current = strrchr(((DirEntry*)memory.discovery.start)[i].path, '.')) == NULL)
continue;
DirEntry* current_dirent = &((DirEntry*)memory.discovery.start)[i];
if(strcmp(current, ".c") == 0)
{
if(c_c > MAX_C_FILES)
DIE("Maximum C file limit was reached with the ibuild project. To extend, you must edit the source code and decide whether there is enough memory.");
d->c_files[c_c] = current_dirent;
c_c++;
}
else if(strcmp(current, ".h") == 0)
{
if(h_c > MAX_H_FILES)
DIE("Maximum H file limit was reached with the ibuild project. To extend, you must edit the source code and decide whether there is enough memory.");
d->h_files[h_c] = current_dirent;
h_c++;
}else if(strcmp(current, ".cpp") == 0)
{
if(cpp_c > MAX_CPP_FILES)
DIE("Maximum CPP file limit was reached with the ibuild project. To extend, you must edit the source code and decide whether there is enough memory.");
d->cpp_files[cpp_c] = current_dirent;
cpp_c++;
}
}
LOG_USER("Found %d C files, %d H files, %d CPP files.", c_c, h_c, cpp_c);
}
/*** compilation process ***/ /*** compilation process ***/
typedef struct typedef struct
@@ -108,6 +241,7 @@ typedef struct
char* build_dir; char* build_dir;
char* target_exec; char* target_exec;
char* src_dir; char* src_dir;
char** src_files;
char* version; char* version;
char* dep_searcher; char* dep_searcher;
char* dep_searcher_flags; char* dep_searcher_flags;
@@ -118,6 +252,8 @@ void launch_compile(Configuration* co)
if(co == NULL) DIE("co has to be valid"); if(co == NULL) DIE("co has to be valid");
if(co->compiler_path == NULL) DIE("requires a valid compiler path"); if(co->compiler_path == NULL) DIE("requires a valid compiler path");
int fildes[2];
pipe(fildes);
pid_t p = fork(); pid_t p = fork();
if(p<0) if(p<0)
@@ -127,9 +263,14 @@ void launch_compile(Configuration* co)
else if (p==0) else if (p==0)
{ {
char* args[] = {co->compiler_path, "ibuild.c", "-o", co->target_exec, NULL}; char* args[] = {co->compiler_path, "ibuild.c", "-o", co->target_exec, NULL};
printf("SET COMPILER: %s\n", co->compiler_path); close(STDOUT_FILENO);
dup(fildes[1]);
close(fildes[0]);
close(fildes[1]);
if((execvp(co->compiler_path, args)) == -1) if((execvp(co->compiler_path, args)) == -1)
DIE("launch_compile() execvp error"); DIE("launch_compile() execvp error");
exit(0);
} }
} }
@@ -180,14 +321,13 @@ bool is_part_of_key(char s)
size_t skip_group(Tokenizer* t, bool (*func)(char)) size_t skip_group(Tokenizer* t, bool (*func)(char))
{ {
if(func == NULL) DIE("func invalid!"); assert(func != NULL);
size_t len = 0; size_t len = 0;
while(func(*t->src)) while(func(*t->src))
{ {
len++; len++;
t->src += 1; t->src += 1;
} }
return len; return len;
} }
@@ -245,6 +385,21 @@ Token tokenizer_next(Tokenizer* t)
return (Token){T_INVALID, NULL}; return (Token){T_INVALID, NULL};
} }
size_t tokenizer_tokenize(Tokenizer* t, Token* ts)
{
size_t t_count = 0;
while(1)
{
if(t_count > MAX_TOKENS) DIE("Maximum number of tokens reached! \n Could not read configuration file.");
Token token = tokenizer_next(t);
if(token.type == T_INVALID) DIE("illegal token!");
ts[t_count] = token;
t_count++;
if(token.type == T_EOF) break;
}
return t_count;
}
/*** configuration parser ***/ /*** configuration parser ***/
typedef enum typedef enum
@@ -345,8 +500,8 @@ Token parser_advance(Parser* p)
Token parser_expect(Parser* p, TokenType tt, char* s) Token parser_expect(Parser* p, TokenType tt, char* s)
{ {
if(p == NULL) DIE("p must not be NULL"); assert(p != NULL);
if(p == NULL) DIE("s must not be NULL"); assert(s != NULL);
if(parser_peek(p, 1).type != tt) if(parser_peek(p, 1).type != tt)
DIE(s); DIE(s);
@@ -396,6 +551,21 @@ Node* parser_parse_next(Parser* p)
return parser_statement(p); return parser_statement(p);
} }
size_t parser_parse(Parser* p, Node** s)
{
assert(p != NULL);
assert(s != NULL);
size_t p_count = 0;
while(p_count < MAX_STATEMENTS && p->loc < p->cap - 1)
{
s[p_count] = parser_parse_next(p);
p_count++;
}
return p_count;
}
#ifdef DEBUG #ifdef DEBUG
void debug_parser(Node* n, int indent) void debug_parser(Node* n, int indent)
@@ -428,12 +598,16 @@ void configuration_set_option(Configuration* co, Key k, char* val)
{ {
switch(k) switch(k)
{ {
case K_NA: DIE("Node with an NA key hit configuration_set_option"); break;
case K_UNKNOWN: DIE("Unknown node encountered!"); break;
case K_COMPILER_PATH: co->compiler_path = val; break; case K_COMPILER_PATH: co->compiler_path = val; break;
case K_BUILD_DIR: co->build_dir = val; break; case K_BUILD_DIR: co->build_dir = val; break;
case K_TARGET_EXEC: co->target_exec = val; break; case K_TARGET_EXEC: co->target_exec = val; break;
case K_DEP_SEARCHER: co->dep_searcher = val; break; case K_DEP_SEARCHER: co->dep_searcher = val; break;
case K_DEP_SEARCHER_FLAGS: co->dep_searcher_flags = val; break; case K_DEP_SEARCHER_FLAGS: co->dep_searcher_flags = val; break;
case K_VERSION: co->version = val; break; case K_VERSION: co->version = val; break;
case K_SRC_DIR: co->src_dir = val; break;
case K_SRC_FILES: DIE("Not implemented!"); break;
default: default:
DIE("Invalid compiler option was supplied!"); DIE("Invalid compiler option was supplied!");
} }
@@ -469,39 +643,29 @@ Configuration configuration_struct_setup(int len, Node** st)
/*** configuration file management ***/ /*** configuration file management ***/
Configuration process_config() Configuration process_config(const char* fn)
{ {
if(!file_has_r_access(CONFIG_FILE)) if(!file_has_r_access(fn))
{ {
return configuration_struct_setup(0, NULL); return configuration_struct_setup(0, NULL);
} }
LOG_USER("IBUILD Configuration file detected.\n"); LOG_USER("IBUILD Configuration file detected.\n");
char* config_mem = file_get_content(&memory.file_contents, CONFIG_FILE); char* config_mem = file_get_content(&memory.file_contents, fn);
size_t t_count = 0;
Token tokens[MAX_TOKENS]; Token tokens[MAX_TOKENS];
Tokenizer t = { .src = config_mem }; Tokenizer t = { .src = config_mem };
while(1) size_t t_count = tokenizer_tokenize(&t, tokens);
if(t_count == 0)
{ {
if(t_count > MAX_TOKENS) DIE("Maximum number of tokens reached! \n Could not read configuration file."); return configuration_struct_setup(0, NULL);
Token token = tokenizer_next(&t);
if(token.type == T_INVALID) DIE("illegal token!");
tokens[t_count] = token;
t_count++;
if(token.type == T_EOF) break;
} }
Parser parser = {.tokens = tokens, .loc = 0, .cap = t_count }; Parser parser = {.tokens = tokens, .loc = 0, .cap = t_count };
Node* statements[MAX_STATEMENTS]; Node* statements[MAX_STATEMENTS];
size_t p_count = 0; size_t p_count = parser_parse(&parser, statements);
while(p_count < MAX_STATEMENTS && parser.loc < parser.cap - 1)
{
statements[p_count] = parser_parse_next(&parser);
p_count++;
}
#ifdef DEBUG #ifdef DEBUG
@@ -513,11 +677,24 @@ Configuration process_config()
return configuration_struct_setup(p_count, statements); return configuration_struct_setup(p_count, statements);
} }
#ifndef TESTING
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
LOG_USER("Build version: %s", VERSION); LOG_USER("Build version: %s", VERSION);
Configuration c = process_config(); Configuration c = process_config(CONFIG_FILE);
launch_compile(&c);
dirent_get_recursive(".");
Discovery d;
d.c_files = (DirEntry**)arena_alloc(&memory.discovery, MAX_C_FILES * sizeof(DirEntry*));
d.h_files = (DirEntry**)arena_alloc(&memory.discovery, MAX_H_FILES * sizeof(DirEntry*));
discover(&d);
// Hand over discovery to the dependency checker.
// Compile
//launch_compile(&c);
memory_free(); memory_free();
return 0; return 0;
} }
#endif