From bb5f514d013ea78819dcc401bbf1712616102fc0 Mon Sep 17 00:00:00 2001 From: 0x221E Date: Wed, 14 Jan 2026 23:56:35 +0100 Subject: [PATCH] Add: Simple recursive file discovery feature and organization of C/Cpp/H files --- deps/utils | 2 +- ibuild.c | 247 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 213 insertions(+), 36 deletions(-) diff --git a/deps/utils b/deps/utils index e5cf1a2..07c0bfe 160000 --- a/deps/utils +++ b/deps/utils @@ -1 +1 @@ -Subproject commit e5cf1a23b3f11fa68006ea2e19b950715dcacea1 +Subproject commit 07c0bfe478b30873ed23e01c4191f082f55371c5 diff --git a/ibuild.c b/ibuild.c index 0e65e62..57651d4 100644 --- a/ibuild.c +++ b/ibuild.c @@ -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 @@ -7,10 +15,8 @@ #include #include #include - -#define CONFIG_FILE "IBUILD" -#define MAX_STATEMENTS 50 -#define MAX_TOKENS 200 +#include +#include #define DIE(fmt, ...) die_t(__func__, __LINE__, fmt, ##__VA_ARGS__) #define LOG_ERROR(fmt, ...) log_write(__func__, fmt, ## __VA_ARGS__) @@ -29,6 +35,7 @@ typedef struct Arena configuration; Arena strings; Arena parser; + Arena discovery; } Memory; static Memory memory; @@ -39,6 +46,7 @@ void memory_free() arena_free(&memory.configuration); arena_free(&memory.strings); arena_free(&memory.parser); + arena_free(&memory.discovery); } /*** logging ***/ @@ -47,12 +55,15 @@ void memory_free() // Include this log in your issues void die_t(const char* func, int line, const char* fmt, ...) { + assert(func != NULL); + assert(fmt != NULL); va_list args; va_start(args, fmt); printf("ibuild exception(f:%s-l:%d): ", func, line); vprintf(fmt, args); printf("\n"); printf("******Last syscall error: %s\n", strerror(errno)); + va_end(args); exit(1); } @@ -72,6 +83,72 @@ void log_write(const char* func, const char* fmt, ...) /*** 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, ¤t, 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) { if(fd == NULL) DIE("fd cannot be NULL."); @@ -100,6 +177,62 @@ bool file_has_r_access(const char* f) 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 ***/ typedef struct @@ -108,6 +241,7 @@ typedef struct char* build_dir; char* target_exec; char* src_dir; + char** src_files; char* version; char* dep_searcher; char* dep_searcher_flags; @@ -117,7 +251,9 @@ void launch_compile(Configuration* co) { if(co == NULL) DIE("co has to be valid"); if(co->compiler_path == NULL) DIE("requires a valid compiler path"); - + + int fildes[2]; + pipe(fildes); pid_t p = fork(); if(p<0) @@ -127,9 +263,14 @@ void launch_compile(Configuration* co) else if (p==0) { 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) 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)) { - if(func == NULL) DIE("func invalid!"); + assert(func != NULL); size_t len = 0; while(func(*t->src)) { len++; t->src += 1; } - return len; } @@ -245,6 +385,21 @@ Token tokenizer_next(Tokenizer* t) 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 ***/ typedef enum @@ -345,8 +500,8 @@ Token parser_advance(Parser* p) Token parser_expect(Parser* p, TokenType tt, char* s) { - if(p == NULL) DIE("p must not be NULL"); - if(p == NULL) DIE("s must not be NULL"); + assert(p != NULL); + assert(s != NULL); if(parser_peek(p, 1).type != tt) DIE(s); @@ -396,6 +551,21 @@ Node* parser_parse_next(Parser* 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 void debug_parser(Node* n, int indent) @@ -428,12 +598,16 @@ void configuration_set_option(Configuration* co, Key k, char* val) { 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_BUILD_DIR: co->build_dir = val; break; case K_TARGET_EXEC: co->target_exec = val; break; case K_DEP_SEARCHER: co->dep_searcher = val; break; case K_DEP_SEARCHER_FLAGS: co->dep_searcher_flags = 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: DIE("Invalid compiler option was supplied!"); } @@ -469,39 +643,29 @@ Configuration configuration_struct_setup(int len, Node** st) /*** 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"); - char* config_mem = file_get_content(&memory.file_contents, CONFIG_FILE); - size_t t_count = 0; - + char* config_mem = file_get_content(&memory.file_contents, fn); + Token tokens[MAX_TOKENS]; 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."); - 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; - } + return configuration_struct_setup(0, NULL); + } Parser parser = {.tokens = tokens, .loc = 0, .cap = t_count }; Node* statements[MAX_STATEMENTS]; - size_t p_count = 0; - - while(p_count < MAX_STATEMENTS && parser.loc < parser.cap - 1) - { - statements[p_count] = parser_parse_next(&parser); - p_count++; - } + size_t p_count = parser_parse(&parser, statements); #ifdef DEBUG @@ -509,15 +673,28 @@ Configuration process_config() debug_parser(statements[i], 0); #endif - - return configuration_struct_setup(p_count, statements); + + return configuration_struct_setup(p_count, statements); } +#ifndef TESTING int main(int argc, char** argv) { LOG_USER("Build version: %s", VERSION); - Configuration c = process_config(); - launch_compile(&c); + Configuration c = process_config(CONFIG_FILE); + + 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(); return 0; } +#endif