From 70c6a4ef328b69d4d89cb146dea9cf95e4d02dff Mon Sep 17 00:00:00 2001 From: Jaromil Date: Sun, 29 Dec 2024 00:40:41 +0100 Subject: [PATCH] fix: now using cjit_add_file, various fixes and improvements correctly executes object files, improved help docs and better code organization --- src/cjit.c | 93 +++++++++++++++++++++++++++++++++------------------ src/main.c | 41 ++++++++++++----------- test/cli.bats | 16 +++++++++ 3 files changed, 99 insertions(+), 51 deletions(-) diff --git a/src/cjit.c b/src/cjit.c index c49dad5..8c9e53e 100644 --- a/src/cjit.c +++ b/src/cjit.c @@ -194,13 +194,30 @@ bool cjit_setup(CJITState *cjit) { return(true); } -bool cjit_add_file(CJITState *cjit, const char *path) { +static int has_source_extension(const char *path) { char *ext; size_t extlen; - bool is_source = false; - bool res = cwk_path_get_extension(path,(const char**)&ext,&extlen); - // no filename extension: still good to add - if(!res) { + bool is_source; + is_source = cwk_path_get_extension(path,(const char**)&ext,&extlen); + //_err("%s: extension: %s",__func__,ext); + if(!is_source) // no extension at all + return 0; + if(extlen==2 && (ext[1]=='c' || ext[1]=='C')) is_source = true; + else if(extlen==3 + && (ext[1]=='c' || ext[1]=='C') + && (ext[2]=='c' || ext[2]=='C')) is_source = true; + else if(extlen==4 + && (ext[1]=='c' || ext[1]=='C') + && (ext[2]=='x' || ext[2]=='X') + && (ext[3]=='x' || ext[3]=='X')) is_source = true; + else is_source = false; + return (is_source? 1 : -1); +} + +bool cjit_add_file(CJITState *cjit, const char *path) { + // _err("%s",__func__); + int is_source = has_source_extension(path); + if(is_source == 0) { // no extension, we still add cjit_setup(cjit); if(tcc_add_file(cjit->TCC, path)<0) { _err("%s: tcc_add_file error",__func__); @@ -208,16 +225,8 @@ bool cjit_add_file(CJITState *cjit, const char *path) { } return true; } - // if C source then use tcc_compile_string - if(extlen==1 && (ext[1]=='c' || ext[1]=='C')) is_source = true; - if(extlen==3 - && (ext[1]=='c' || ext[1]=='C') - && (ext[2]=='c' || ext[2]=='C')) is_source = true; - if(extlen==4 - && (ext[1]=='c' || ext[1]=='C') - && (ext[2]=='x' || ext[2]=='X') - && (ext[3]=='x' || ext[3]=='X')) is_source = true; - if(is_source) { + if(is_source>0) { + // _err("%s: is source(%i): %s",__func__, is_source, path); long length = file_size(path); if (length == -1) return false; FILE *file = fopen(path, "r"); @@ -235,6 +244,15 @@ bool cjit_add_file(CJITState *cjit, const char *path) { contents[length] = 0x0; // Null-terminate the string fclose(file); cjit_setup(cjit); + size_t dirname; + cwk_path_get_dirname(path,&dirname); + if(dirname) { + char *tmp = malloc(dirname+1); + strncpy(tmp,path,dirname); + tmp[dirname] = 0x0; + tcc_add_include_path(cjit->TCC,tmp); + free(tmp); + } tcc_compile_string(cjit->TCC,contents); free(contents); } else { @@ -248,32 +266,43 @@ bool cjit_add_file(CJITState *cjit, const char *path) { } bool cjit_compile_file(CJITState *cjit, const char *path) { - char *restrict tmp; - const char *basename; - char *ext; - size_t len; - cwk_path_get_basename((char*)path, &basename, &len); - tmp = malloc(len+2); - strncpy(tmp,basename,len+1); + int is_source = has_source_extension(path); // _err("basename: %s",tmp); - if( !cwk_path_get_extension(tmp,(const char**)&ext,&len) ) { + if( is_source == 0) { _err("%s: filename has no extension: %s", __func__,basename); - return 1; + return false; } - // _err("extension: %s",ext); - if( *(ext+1) != 'c' ) { + if( is_source < 0) { _err("%s: filename has wrong extension: %s", __func__,basename); - return 1; + return false; } - strcpy(ext,".o"); cjit_setup(cjit); tcc_add_file(cjit->TCC, path); - _err("Compiling: %s -> %s",path,tmp); - tcc_output_file(cjit->TCC,tmp); - free(tmp); - return 0; + if(cjit->output_filename) { + if(!cjit->quiet) + _err("Compiling: %s -> %s",path, + cjit->output_filename); + tcc_output_file(cjit->TCC, + cjit->output_filename); + } else { + char *ext; + size_t extlen; + char *restrict tmp; + const char *basename; + size_t len; + cwk_path_get_basename((char*)path, &basename, &len); + tmp = malloc(len+2); + strncpy(tmp,basename,len+1); + cwk_path_get_extension(tmp,(const char**)&ext,&extlen); + strcpy(ext,".o"); + if(!cjit->quiet) + _err("Compiling: %s -> %s",path,tmp); + tcc_output_file(cjit->TCC,tmp); + free(tmp); + } + return true; } int cjit_exec(CJITState *cjit, int argc, char **argv) { diff --git a/src/main.c b/src/main.c index 4639689..0a47c89 100644 --- a/src/main.c +++ b/src/main.c @@ -60,25 +60,28 @@ static int parse_value(char *str) { } const char cli_help[] = - "\n" - "Synopsis: cjit [options] files(*) -- app arguments\n" - " (*) can be any source (.c) or built object (dll, dylib, .so)\n" - "Options:\n" - " -h \t print this help\n" - " -v \t print version information\n" - " -q \t stay quiet and only print errors and output\n" - " -D sym\t define a macro symbol or key=value\n" - " -C \t set compiler flags (default from env var CFLAGS)\n" - " -I dir\t also search folder 'dir' for header files\n" - " -l lib\t search the library named 'lib' when linking\n" - " -L dir\t also search inside folder 'dir' for -l libs\n" - " -e fun\t entry point function (default 'main')\n" - " -p pid\t write pid of executed program to file\n" - " --temp\t create the runtime temporary dir and exit\n" + "CJIT %s by Dyne.org\n" + "\n" + "Synopsis: cjit [options] files(*) -- app arguments\n" + " (*) can be any source (.c) or built object (dll, dylib, .so)\n" + "Options:\n" + " -h \t print this help\n" + " -v \t print version information\n" + " -q \t stay quiet and only print errors and output\n" + " -D sym\t define a macro symbol or key=value\n" + " -C \t set compiler flags (default from env var CFLAGS)\n" + " -I dir\t also search folder 'dir' for header files\n" + " -l lib\t search the library named 'lib' when linking\n" + " -L dir\t also search inside folder 'dir' for -l libs\n" + " -e fun\t entry point function (default 'main')\n" + " -p pid\t write pid of executed program to file\n" + " -c \t compile a single source file, do not execute\n" + " -o exe\t compile to an 'exe' file, do not execute\n" + " --temp\t create the runtime temporary dir and exit\n" #if defined(SELFHOST) - " --src\t extract source code to cjit_source\n" + " --src\t extract source code to cjit_source\n" #endif - " --xtgz\t extract all contents from a USTAR tar.gz\n"; + " --xtgz\t extract all contents from a USTAR tar.gz\n"; int main(int argc, char **argv) { @@ -101,7 +104,7 @@ int main(int argc, char **argv) { }; ketopt_t opt = KETOPT_INIT; // tolerated and ignored: -f -W -O -g -U -E -S -M - while ((c = ketopt(&opt, argc, argv, 1, "qhvD:L:l:C:I:e:p:co:f:W:O:gU:ESM:", longopts)) >= 0) { + while ((c = ketopt(&opt, argc, argv, 1, "qhvD:L:l:C:I:e:p:co:f:W:O:gU:ESM:m:", longopts)) >= 0) { if(c == 'q') { CJIT->quiet = true; } @@ -284,7 +287,7 @@ int main(int argc, char **argv) { // 3 : BOM found, UTF8 if(res ==0) { cjit_setup(CJIT); - tcc_add_file(CJIT->TCC, code_path); + cjit_add_file(CJIT, code_path); } else if(res<0) { _err("Cannot open file: %s",code_path); _err("Execution aborted."); diff --git a/test/cli.bats b/test/cli.bats index d6b59d9..7d6f37a 100644 --- a/test/cli.bats +++ b/test/cli.bats @@ -18,6 +18,22 @@ load bats_setup assert_output --partial 'Please compile with -DALLOWED=1' } +@test "Compile to object and execute" { + run ${CJIT} -c test/hello.c + assert_success + run ${CJIT} hello.o + assert_success + assert_output 'Hello World!' +} + +@test "Compile to custom object and execute" { + run ${CJIT} -o world.o -c test/hello.c + assert_success + run ${CJIT} world.o + assert_success + assert_output 'Hello World!' +} + @test "Execute multiple files" { run ${CJIT} -q test/multifile/* assert_success