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>
This commit is contained in:
parent
f6e8b0f73c
commit
6b995e1582
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,3 +1,2 @@
|
||||
tbuild
|
||||
main.o
|
||||
|
||||
build
|
||||
out
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
|
||||
|
4
Makefile
4
Makefile
@ -1,5 +1,5 @@
|
||||
CC=tcc
|
||||
CFLAGS=-O0 -ggdb -Wall
|
||||
CC=cc
|
||||
CFLAGS=-O0 -ggdb -Wall -lpthread
|
||||
|
||||
.PHONY: all clean install
|
||||
|
||||
|
327
main.c
327
main.c
@ -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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// FIXME: Make this cross platform.
|
||||
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("Error: Cannot build project: Cannot create build directory.\n", stderr);
|
||||
free_manifest(manifest);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
int ret = build_project(project_path, options);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
text_format("%s/lib", project_path);
|
||||
char *lib_dir = malloc((strlen(buffer_text_format)+1)*sizeof(char));
|
||||
strcpy(lib_dir, buffer_text_format);
|
||||
|
||||
// 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;
|
||||
}
|
||||
x_watcher *watcher = xWatcher_create();
|
||||
|
||||
struct {
|
||||
char *project_path;
|
||||
build_options options;
|
||||
} dat = {
|
||||
project_path,
|
||||
options
|
||||
};
|
||||
|
||||
xWatcher_reference src;
|
||||
src.path = src_dir;
|
||||
src.callback_func = watcher_cb;
|
||||
src.context = 1;
|
||||
src.additional_data = &dat;
|
||||
xWatcher_appendDir(watcher, &src);
|
||||
|
||||
xWatcher_reference lib;
|
||||
lib.path = lib_dir;
|
||||
lib.callback_func = watcher_cb;
|
||||
lib.context = 2;
|
||||
lib.additional_data = &dat;
|
||||
xWatcher_appendDir(watcher, &lib);
|
||||
|
||||
xWatcher_start(watcher);
|
||||
|
||||
puts(YELLOW "Listening for file changes..." RESET);
|
||||
getchar();
|
||||
|
||||
xWatcher_destroy(watcher);
|
||||
|
||||
free(src_dir);
|
||||
}
|
||||
|
||||
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("Error: Cannot build project: Cannot create output directory.\n", 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
1
x-watcher
Submodule
1
x-watcher
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 01e9a4b6b051f240fc68eb0a0e3085c4a209dd31
|
Loading…
x
Reference in New Issue
Block a user