diff options
author | xSlendiX <slendi@socopon.com> | 2022-12-24 18:49:14 +0200 |
---|---|---|
committer | xSlendiX <slendi@socopon.com> | 2022-12-24 18:51:46 +0200 |
commit | 6b995e15829801e91b6deb13b4fa026dcffd5fe6 (patch) | |
tree | 020241d6032478a145f7ddb52aed001178a0625b | |
parent | f6e8b0f73c2485d2c3766209fa487917e3e5254b (diff) |
Add file watching and colour coding.
This patch adds a new option to building: -w. What this option does is
it runs the build system on any change detected in either src/ or lib/.
This is a really convenient option since you don't need to type "tbuild
b" every time.
Signed-off-by: xSlendiX <slendi@socopon.com>
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | main.c | 321 | ||||
m--------- | x-watcher | 0 |
5 files changed, 218 insertions, 115 deletions
@@ -1,3 +1,2 @@ -tbuild -main.o - +build +out diff --git a/.gitmodules b/.gitmodules index f033c8c..1dd901f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "tomlc99"] path = tomlc99 url = https://github.com/cktan/tomlc99 +[submodule "x-watcher"] + path = x-watcher + url = https://github.com/nikp123/x-watcher @@ -1,5 +1,5 @@ -CC=tcc -CFLAGS=-O0 -ggdb -Wall +CC=cc +CFLAGS=-O0 -ggdb -Wall -lpthread .PHONY: all clean install @@ -12,6 +12,33 @@ #include <stdarg.h> #include "tomlc99/toml.h" +#include "x-watcher/x-watcher.h" + +#if defined(_WIN32) +// FIXME: Find a way to do ANSI on Windows. +#define RED "" +#define GREEN "" +#define YELLOW "" +#define BLUE "" +#define MAGENTA "" +#define CYAN "" + +#define RESET "" +#else +#define RED "\e[31m" +#define GREEN "\e[32m" +#define YELLOW "\e[33m" +#define BLUE "\e[34m" +#define MAGENTA "\e[35m" +#define CYAN "\e[36m" + +#define RESET "\e[0m" +#endif + +typedef struct { + bool zeal_build; + bool watch; +} build_options; // FIXME: This is hardcoded, fix it. char *python_interpreter_path = "/usr/local/bin/python3"; @@ -241,31 +268,31 @@ project_manifest* load_manifest(char const *path) { toml_table_t *conf = toml_parse_file(fp, errbuf, sizeof(errbuf)); fclose(fp); if (!conf) { - fprintf(stderr, "Error: Cannot load manifest file: Cannot parse file: %s\n", errbuf); + fprintf(stderr, RED "Error: Cannot load manifest file: Cannot parse file: %s\n" RESET, errbuf); return NULL; } toml_table_t *general = toml_table_in(conf, "General"); if (!general) { - fprintf(stderr, "Error: Cannot load manifest file: Cannot find [General] table.\n"); + fprintf(stderr, RED "Error: Cannot load manifest file: Cannot find [General] table.\n" RESET); return NULL; } toml_datum_t name = toml_string_in(general, "Name"); if (!name.ok) { - fprintf(stderr, "Error: Cannot load manifest file: Cannot find Name field.\n"); + fprintf(stderr, RED "Error: Cannot load manifest file: Cannot find Name field.\n" RESET); return NULL; } toml_datum_t author = toml_string_in(general, "Author"); if (!author.ok) { - fprintf(stderr, "Error: Cannot load manifest file: Cannot find Author field.\n"); + fprintf(stderr, RED "Error: Cannot load manifest file: Cannot find Author field.\n" RESET); return NULL; } toml_datum_t version = toml_string_in(general, "Version"); if (!version.ok) { - fprintf(stderr, "Error: Cannot load manifest file: Cannot find Version field.\n"); + fprintf(stderr, RED "Error: Cannot load manifest file: Cannot find Version field.\n" RESET); return NULL; } @@ -278,7 +305,7 @@ project_manifest* load_manifest(char const *path) { // FIXME: Add dependencies. toml_table_t *dependencies = toml_table_in(conf, "Dependencies"); if (!dependencies) { - fprintf(stderr, "Error: Cannot load manifest file: Cannot find [Dependencies] table.\n"); + fprintf(stderr, RED "Error: Cannot load manifest file: Cannot find [Dependencies] table.\n" RESET); return NULL; } @@ -383,9 +410,9 @@ void print_help(char **argv) { printf("Usage: %s [command]\n", argv[0]); fputs("\nCommands:\n", stderr); fputs(" * init|i [path=.] - Setup a new project.\n", stderr); - fputs(" * build|. [--zeal|-z|-Z] - Build project in current working directory.\n", stderr); + fputs(" * build|. [--zeal|-z|-Z] [--watch|-w|-W] - Build project in current working directory.\n", stderr); fputs(" * clean - Clean output code in current working directory.\n", stderr); - fputs("\nTo see manifest file usage, check out man tbuild(1)\n", stderr); + fputs("\nTo read more about the manifest file, check out tbuild_manifest(1).\n", stderr); } bool file_exists(char const *path) { @@ -400,7 +427,7 @@ bool makedir(char const *path) { if (stat(path, &st) == -1) { mkdir(path, 0700); } else { - fprintf(stderr, "Warning: File already exists. Continuing anyway."); + fprintf(stderr, YELLOW "Warning: File already exists. Continuing anyway." RESET); return false; } return true; @@ -480,7 +507,7 @@ void convert_to_zealos(char const *path) { if (fpath[len-2] != 'H' || fpath[len-3] != '.') continue; - printf("Converting %s\n", entry->d_name); + printf(" -> Converting %s\n", entry->d_name); // Credit: ZealOS ConversionScript. replace_in_file(fpath, "MemCpy", "MemCopy"); @@ -607,7 +634,7 @@ bool run_scripts(char const *path) { if (fpath[len-3] != 'p' || fpath[len-2] != 'y' || fpath[len-3] != '.') continue; - printf("Running %s\n", entry->d_name); + printf(" -> Running %s\n", entry->d_name); char *argv[] = { fpath, NULL @@ -621,6 +648,133 @@ bool run_scripts(char const *path) { return true; } +int build_project(char *project_path, build_options options) { + int i; + + puts("Starting build..."); + + project_manifest* manifest = load_manifest(text_format("%s/" MANIFEST_FNAME, project_path)); + if (!manifest) { + fputs(RED "Error: Cannot build project: Failed to open manifest.\n" RESET, stderr); + return 1; + } + + text_format("%s/lib", project_path); + char *lib_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char)); + strcpy(lib_dir, buffer_text_format); + if (!file_exists(lib_dir)) { + bool status = makedir(lib_dir); + if (!status) { + fputs(RED "Error: Cannot build project: Cannot create lib directory.\n" RESET, stderr); + free_manifest(manifest); + return 1; + } + } + + for (i = 0; i < manifest->dependencies_amount; i++) { + dependency dep = manifest->dependencies[i]; + printf("%s -> %s (%i)\n", dep.name, dep.uri, i); + + puts(text_format("%s/%s", lib_dir, dep.name)); + if (!file_exists(text_format("%s/%s", lib_dir, dep.name))) + system(text_format("git clone --depth 1 --recursive --shallow-submodules '%s' '%s/%s'", dep.uri, lib_dir, dep.name)); + else + system(text_format("cd '%s/%s' && git pull", lib_dir, dep.name)); + } + + // Create build directory + text_format("%s/build", project_path); + char *build_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char)); + strcpy(build_dir, buffer_text_format); + if (!file_exists(build_dir)) { + bool status = makedir(build_dir); + if (!status) { + fputs(RED "Error: Cannot build project: Cannot create build directory.\n" RESET, stderr); + free_manifest(manifest); + return 1; + } + } + + text_format("%s/src", project_path); + char *src_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char)); + strcpy(src_dir, buffer_text_format); + + // Clear and populate. + puts(" -> Populating build directory..."); + clear_directory(build_dir); + copy_directory(src_dir, build_dir); + if (file_exists(lib_dir)) + copy_directory(lib_dir, build_dir); + + // Run scripts (if any) + puts(" -> Running scripts..."); + // FIXME: This is incredibly hacky. + text_format("%s/scripts", project_path); + char *scripts_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char)); + strcpy(scripts_dir, buffer_text_format); + if (file_exists(scripts_dir)) { + if (!run_scripts(scripts_dir)) { + fputs(RED "Failed running script!\b" RESET, stderr); + return 1; + } + } + + if (options.zeal_build) { + puts(" -> Converting to ZealOS..."); + convert_to_zealos(build_dir); + } + + // TODO: Obfuscate code if enabled. + + text_format("%s/output", project_path); + char *out_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char)); + strcpy(out_dir, buffer_text_format); + if (!file_exists(out_dir)) { + bool status = makedir(out_dir); + if (!status) { + fputs(RED "Error: Cannot build project: Cannot create output directory.\n" RESET, stderr); + free_manifest(manifest); + return 1; + } + } + + if (options.zeal_build) + text_format("%s/output/%s-%s.zeal.ISO.C", project_path, manifest->name, manifest->version); + else + text_format("%s/output/%s-%s.ISO.C", project_path, manifest->name, manifest->version); + + puts(" -> Generating ISO.C"); + char *iso_c = malloc((strlen(buffer_text_format)+1)*sizeof(char)); + strcpy(iso_c, buffer_text_format); +#if defined(_WIN32) + system(text_format("RedSeaGen.exe '%s' '%s'", build_dir, iso_c)); +#else + system(text_format("RedSeaGen '%s' '%s'", build_dir, iso_c)); +#endif + + free(build_dir); + free(out_dir); + free(scripts_dir); + free(lib_dir); + free_manifest(manifest); + + puts(GREEN "Build finished!" RESET); + + return 0; +} + +void watcher_cb(XWATCHER_FILE_EVENT event, char const *path, int context, void *data) { + if (event != XWATCHER_FILE_MODIFIED) + return; + + puts(YELLOW "File change detected!" RESET); + struct { + char *project_path; + build_options options; + } *dat = data; + build_project(dat->project_path, dat->options); +} + int main(int argc, char **argv) { if (argc < 2) { puts("No arguments provided.\n"); @@ -650,20 +804,20 @@ int main(int argc, char **argv) { strcpy(project_path, argv[2]); } - fprintf(stderr, "Initializing project in `%s`.\n", project_path); + fprintf(stderr, GREEN "Initializing project in `%s`.\n" RESET, project_path); bool ret = true; if (!(strlen(project_path) == 1 && project_path[0] == '.')) ret = makedir_parenting(project_path); if (!ret) { - fputs("Error: Cannot create project: Cannot create directories.\n", stderr); + fputs(RED "Error: Cannot create project: Cannot create directories.\n" RESET, stderr); return -1; } // Check if manifest file exists. if (file_exists(text_format("%s/" MANIFEST_FNAME, project_path))) { - fputs("Error: Cannot create project: Project already exists!\n", stderr); + fputs(RED "Error: Cannot create project: Project already exists!\n" RESET, stderr); return -1; } @@ -737,9 +891,7 @@ Del(\"C:/Apps/Main/RunCD.*\");\n\ } else if (cmd == 'b' || cmd == '.') { int i; - struct { - bool zeal_build; - } options = { 0 }; + build_options options = { 0 }; for (int i = 2; i < argc; i++) { if (argv[i][0] != '-') @@ -748,6 +900,8 @@ Del(\"C:/Apps/Main/RunCD.*\");\n\ if (argv[i][1] == '-') { if (strcmp(argv[i], "--zeal")) options.zeal_build = true; + if (strcmp(argv[i], "--watch")) + options.watch = true; continue; } @@ -758,6 +912,10 @@ Del(\"C:/Apps/Main/RunCD.*\");\n\ case 'Z': options.zeal_build = true; break; + case 'w': + case 'W': + options.watch = true; + break; } } } @@ -775,111 +933,54 @@ Del(\"C:/Apps/Main/RunCD.*\");\n\ return 1; } - project_manifest* manifest = load_manifest(text_format("%s/" MANIFEST_FNAME, project_path)); - if (!manifest) { - fputs("Error: Cannot build project: Failed to open manifest.\n", stderr); - return 1; - } - puts(buffer_text_format); + int ret = build_project(project_path, options); + if (ret != 0) + return ret; - text_format("%s/lib", project_path); - char *lib_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char)); - strcpy(lib_dir, buffer_text_format); - if (!file_exists(lib_dir)) { - bool status = makedir(lib_dir); - if (!status) { - fputs("Error: Cannot build project: Cannot create lib directory.\n", stderr); - free_manifest(manifest); - return 1; - } - } + if (options.watch) { + text_format("%s/src", project_path); + char *src_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char)); + strcpy(src_dir, buffer_text_format); - for (i = 0; i < manifest->dependencies_amount; i++) { - dependency dep = manifest->dependencies[i]; - printf("%s -> %s (%i)\n", dep.name, dep.uri, i); + text_format("%s/lib", project_path); + char *lib_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char)); + strcpy(lib_dir, buffer_text_format); - puts(text_format("%s/%s", lib_dir, dep.name)); - if (!file_exists(text_format("%s/%s", lib_dir, dep.name))) - system(text_format("git clone --depth 1 --recursive --shallow-submodules '%s' '%s/%s'", dep.uri, lib_dir, dep.name)); - else - // FIXME: Make this cross platform. - system(text_format("cd '%s/%s' && git pull", lib_dir, dep.name)); - } + x_watcher *watcher = xWatcher_create(); - // Create build directory - text_format("%s/build", project_path); - char *build_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char)); - strcpy(build_dir, buffer_text_format); - if (!file_exists(build_dir)) { - bool status = makedir(build_dir); - if (!status) { - fputs("Error: Cannot build project: Cannot create build directory.\n", stderr); - free_manifest(manifest); - return 1; - } - } + struct { + char *project_path; + build_options options; + } dat = { + project_path, + options + }; - text_format("%s/src", project_path); - char *src_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char)); - strcpy(src_dir, buffer_text_format); + xWatcher_reference src; + src.path = src_dir; + src.callback_func = watcher_cb; + src.context = 1; + src.additional_data = &dat; + xWatcher_appendDir(watcher, &src); - // Clear and populate. - puts("Populating build directory..."); - clear_directory(build_dir); - copy_directory(src_dir, build_dir); - if (file_exists(lib_dir)) - copy_directory(lib_dir, build_dir); - - // Run scripts (if any) - puts("Running scripts..."); - // FIXME: This is incredibly hacky. - text_format("%s/scripts", project_path); - char *scripts_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char)); - strcpy(scripts_dir, buffer_text_format); - if (file_exists(scripts_dir)) { - if (!run_scripts(scripts_dir)) { - fputs("Failed running script!\b", stderr); - return -1; - } - } + xWatcher_reference lib; + lib.path = lib_dir; + lib.callback_func = watcher_cb; + lib.context = 2; + lib.additional_data = &dat; + xWatcher_appendDir(watcher, &lib); - if (options.zeal_build) { - puts("Converting to ZealOS..."); - convert_to_zealos(build_dir); - } + xWatcher_start(watcher); - // TODO: Obfuscate code if enabled. + puts(YELLOW "Listening for file changes..." RESET); + getchar(); - text_format("%s/output", project_path); - char *out_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char)); - strcpy(out_dir, buffer_text_format); - if (!file_exists(out_dir)) { - bool status = makedir(out_dir); - if (!status) { - fputs("Error: Cannot build project: Cannot create output directory.\n", stderr); - free_manifest(manifest); - return 1; - } + xWatcher_destroy(watcher); + + free(src_dir); } - if (options.zeal_build) - text_format("%s/output/%s-%s.zeal.ISO.C", project_path, manifest->name, manifest->version); - else - text_format("%s/output/%s-%s.ISO.C", project_path, manifest->name, manifest->version); - char *iso_c = malloc((strlen(buffer_text_format)+1)*sizeof(char)); - strcpy(iso_c, buffer_text_format); -#if defined(_WIN32) - system(text_format("RedSeaGen.exe '%s' '%s'", build_dir, iso_c)); -#else - system(text_format("RedSeaGen '%s' '%s'", build_dir, iso_c)); -#endif -//abs_exe_path, - free(build_dir); - free(out_dir); - free(scripts_dir); - free(lib_dir); free(project_path); - free_manifest(manifest); } } diff --git a/x-watcher b/x-watcher new file mode 160000 +Subproject 01e9a4b6b051f240fc68eb0a0e3085c4a209dd3 |