summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsaw.c549
1 files changed, 328 insertions, 221 deletions
diff --git a/saw.c b/saw.c
index 9ab93ec..6560fe0 100755
--- a/saw.c
+++ b/saw.c
@@ -15,14 +15,39 @@
#/ - No configuration required
#/ - Cross-platform thanks to sokol and miniaudio
#/
-#/ NOTE
-#/ You can build the project easily using this two commands:
-#/ > gcc -o buildme -DBUILDME saw.c
+#/ ----------------------------------------------------------------
+#/
+#/ NOTE Automatic self-compilation
+#/
+#/ If you have a Linux shell, you can build the project by running
+#/ > ./saw.c
+#/
+#/ Or you can build the project easily using those two commands:
+#/ > gcc -o buildme -DBUILDME=1 saw.c
#/ > ./buildme
#/ Or, on Windows, with MSVC compiler:
-#/ > cl.exe -Febuildme.exe -DBUILDME saw.c
+#/ > cl.exe -Febuildme.exe -DBUILDME=1 saw.c
#/ > buildme.exe
#/
+#/ NOTE Manual compilation
+#/
+#/ - Code generation
+#/ > gcc -o build_codegen -DCODEGEN=1 saw.c
+#/ > ./build_codegen
+#/
+#/ - Dependencies
+#/ > gcc -c -o build/thirdparty.o -DDEPENDENCIES=1 saw.c
+#/
+#/ - Test suite
+#/ > gcc -o build/test_suite -DTESTS=1
+#/ > build/test_suite
+#/
+#/ - Executable
+#/ > gcc -o build/saw -DEXE=1
+#/ > build/saw
+#/
+#/ ----------------------------------------------------------------
+#/
#/ To-Do list
#/
#/ - Code
@@ -143,11 +168,12 @@ exit $? # */
//
// ================================================================
// Define one of the options for the linter
-#if !BUILDME && \
+#if !CODEGEN && \
+ !BUILDME && \
!DEPENDENCIES && \
!EXE && \
!TESTS
-#define EXE 1
+#define BUILDME 1
#endif
#define _GNU_SOURCE
// ================================================================
@@ -156,19 +182,153 @@ exit $? # */
//
// ================================================================
-#if BUILDME
+#if CODEGEN || BUILDME
-typedef int i32;
-typedef long long i64;
-typedef unsigned u32;
-typedef char c8;
-typedef signed char b8;
+enum {
+ LINUX,
+ WINDOWS,
+ MACOS,
+};
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define DLM "\\"
+#define OS WINDOWS
+#elif defined(__APPLE__)
+#define DLM "/"
+#define OS MACOS
+#else // assume Linux
+#define DLM "/"
+#define OS LINUX
+#endif
+
+#define FONT_TEXT "fonts" DLM "domitian_roman.ttf"
+#define FONT_ICONS "fonts" DLM "font_awesome_6_free_solid_900.ttf"
+#define INL_FONTS "build_fonts.inl.c"
+#define SOURCE "saw.c"
+#define PROJECT "saw"
+
+typedef int i32;
+typedef long long i64;
+typedef unsigned u32;
+typedef unsigned long long u64;
+typedef char c8;
+typedef signed char b8;
#include <stdio.h>
#include <stdlib.h>
+
+enum {
+ MAX_LENGTH = 200,
+};
+
+i64 int_len(u32 x) {
+ i64 len = 0;
+
+ do {
+ x /= 10;
+ ++len;
+ } while (x > 0);
+
+ return len;
+}
+
+i64 print_bytes(FILE *out, FILE *in) {
+ i64 size = 0, line_len = MAX_LENGTH;
+
+ while (!feof(in)) {
+ u32 x = 0;
+
+ i64 n = fread(&x, 1, sizeof x, in);
+ if (n <= 0)
+ break;
+
+ i64 len = int_len(x);
+
+ line_len += len + 2;
+
+ if (line_len >= MAX_LENGTH) {
+ fprintf(out, "\n ");
+ line_len = 3 + len;
+ }
+
+ fprintf(out, " %u,", x);
+
+ size += n;
+ }
+
+ return size;
+}
+
+void codegen() {
+ printf("Code generation\n");
+ fflush(stdout);
+
+ FILE *out = fopen(INL_FONTS, "wb");
+
+ if (out == NULL) {
+ printf("Unable to write `%s`", INL_FONTS);
+ exit(-1);
+ }
+
+ fprintf(out, "// Saw generated code\n\n");
+ fprintf(out, "#include \"kit/types.h\"\n\n");
+
+ {
+ FILE *in = fopen(FONT_TEXT, "rb");
+
+ if (in == NULL) {
+ printf("Unable to read `%s`", FONT_TEXT);
+ exit(-1);
+ }
+
+ fprintf(out, "u32 ttf_text[] = {");
+
+ i64 n = print_bytes(out, in);
+
+ fprintf(out, "\n};\n\n");
+ fprintf(out, "enum { TTF_TEXT_SIZE = %lld, };\n\n", n);
+
+ fclose(in);
+ }
+
+ {
+ FILE *in = fopen(FONT_ICONS, "rb");
+
+ if (in == NULL) {
+ printf("Unable to read `%s`", FONT_ICONS);
+ exit(-1);
+ }
+
+
+ fprintf(out, "u32 ttf_icons[] = {");
+
+ i64 n = print_bytes(out, in);
+
+ fprintf(out, "\n};\n\n");
+ fprintf(out, "enum { TTF_ICONS_SIZE = %lld, };\n\n", n);
+
+ fclose(in);
+ }
+
+ fclose(out);
+}
+
+#endif
+
+#if CODEGEN
+
+int main(int argc, char **argv) {
+ (void) argc;
+ (void) argv;
+
+ codegen();
+ return 0;
+}
+
+#elif BUILDME
+
#include <string.h>
#include <stdarg.h>
-#include <assert.h>
#if defined(_WIN32) && !defined(__CYGWIN__)
# define WIN32_LEAN_AND_MEAN
@@ -178,9 +338,6 @@ typedef signed char b8;
# include <sys/stat.h>
#endif
-#define PROJECT "saw"
-#define SOURCE "saw.c"
-
#define REQUIRE_MATH 1
#define REQUIRE_DL 0
#define REQUIRE_THREADS 0
@@ -197,23 +354,6 @@ typedef signed char b8;
// TODO Add command line option for the C runtime.
enum {
- LINUX,
- WINDOWS,
- MACOS,
-};
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#define DLM "\\"
-#define OS WINDOWS
-#elif defined(__APPLE__)
-#define DLM "/"
-#define OS MACOS
-#else // assume Linux
-#define DLM "/"
-#define OS LINUX
-#endif
-
-enum {
BUFFER_COUNT = 16,
BUFFER_SIZE = 512,
STRING_COUNT = 64,
@@ -238,7 +378,11 @@ c8 *flag_exe = "-o ";
c8 *flags = "";
c8 *link_flags = "";
-b8 run_tests = 1;
+b8 rebuild_all = 0;
+b8 rebuild_exe = 0;
+b8 run_codegen = 0;
+b8 run_tests = 1;
+b8 run_exe = 0;
void print_help(void) {
printf(
@@ -250,7 +394,11 @@ void print_help(void) {
" -d --destination - Set destination path\n"
" -o --options - Set additional compiler options\n"
" -l --link - Set additional linker options\n"
- " -s --skiptests - Do not run tests\n\n"
+ " -e --rebuild - Rebuild executable\n"
+ " -r --rebuildall - Rebuild dependencies\n"
+ " -g --codegen - Run code generation\n"
+ " -s --skiptests - Do not run tests\n"
+ " -x --run - Run the executable\n\n"
);
fflush(stdout);
}
@@ -264,7 +412,10 @@ c8 lowercase_char(c8 c) {
c8 *lowercase(c8 *s) {
i32 i = 0;
for (; s[i] != '\0'; ++i) {
- assert(i + 1 < BUFFER_SIZE);
+ if (i + 1 >= BUFFER_SIZE) {
+ printf("Buffer overflow\n");
+ exit(-1);
+ }
_buffers[_buffer_index][i] = lowercase_char(s[i]);
}
_buffers[_buffer_index][i] = '\0';
@@ -294,7 +445,10 @@ void fmt_dup_v(c8 *format, va_list args) {
c8 *fmt_dup(c8 *format, ...) {
va_list args;
va_start(args, format);
- assert(_string_index < STRING_COUNT);
+ if (_string_index >= STRING_COUNT) {
+ printf("Buffer overflow\n");
+ exit(-1);
+ }
fmt_v(format, args);
c8 *result = _strings[_string_index++];
va_end(args);
@@ -334,6 +488,52 @@ b8 file_exists(c8 *name) {
return 0;
}
+typedef struct {
+ u64 seconds;
+ u32 nanoseconds;
+} Timestamp;
+
+Timestamp file_timestamp(c8 *name) {
+ Timestamp time = {
+ .seconds = 0,
+ .nanoseconds = 0,
+ };
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ HANDLE f = CreateFileA(buf, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ FILETIME ft;
+ if (f != INVALID_HANDLE_VALUE && GetFileTime(f, NULL, NULL, &ft) != 0) {
+ u64 nsec100 = (((u64) ft.dwHighDateTime) << 32) | (u64) ft.dwLowDateTime;
+ time = (Timestamp) {
+ .seconds = (u64) (nsec100 / 10000000),
+ .nanoseconds = (u32) (100 * (nsec100 % 10000000)),
+ };
+ }
+ if (f != INVALID_HANDLE_VALUE)
+ CloseHandle(f);
+#else
+ struct stat info;
+ if (stat(name, &info) == 0 && (S_ISREG(info.st_mode) || S_ISDIR(info.st_mode)))
+ time = (Timestamp) {
+ #ifdef st_mtime
+ .seconds = (u64) info.st_mtim.tv_sec,
+ .nanoseconds = (u32) info.st_mtim.tv_nsec,
+ #else
+ .seconds = (u64) info.st_mtime,
+ .nanoseconds = 0,
+ #endif
+ };
+#endif
+
+ return time;
+}
+
+b8 time_less(Timestamp a, Timestamp b) {
+ if (a.seconds == b.seconds)
+ return a.nanoseconds < b.nanoseconds;
+ return a.seconds < b.seconds;
+}
+
void create_folder_recursive(c8 *path) {
#if defined(_WIN32) && !defined(__CYGWIN__)
system(fmt("if not exist %s mkdir %s", destination, destination));
@@ -342,48 +542,6 @@ void create_folder_recursive(c8 *path) {
#endif
}
-enum {
- MAX_LENGTH = 200,
-};
-
-i64 int_len(u32 x) {
- i64 len = 0;
-
- do {
- x /= 10;
- ++len;
- } while (x > 0);
-
- return len;
-}
-
-i64 print_bytes(FILE *out, FILE *in) {
- i64 size = 0, line_len = MAX_LENGTH;
-
- while (!feof(in)) {
- u32 x = 0;
-
- i64 n = fread(&x, 1, sizeof x, in);
- if (n <= 0)
- break;
-
- i64 len = int_len(x);
-
- line_len += len + 2;
-
- if (line_len >= MAX_LENGTH) {
- fprintf(out, "\n ");
- line_len = 3 + len;
- }
-
- fprintf(out, " %u,", x);
-
- size += n;
- }
-
- return size;
-}
-
i32 main(i32 argc, c8 **argv) {
// Handle command line arguments
//
@@ -409,8 +567,16 @@ i32 main(i32 argc, c8 **argv) {
extra_options = argv[++i];
else if (str_eq_lower(opt, "link"))
extra_link_options = argv[++i];
+ else if (str_eq_lower(opt, "rebuild"))
+ rebuild_exe = 1;
+ else if (str_eq_lower(opt, "rebuildall"))
+ rebuild_all = 1;
+ else if (str_eq_lower(opt, "codegen"))
+ run_codegen = 1;
else if (str_eq_lower(opt, "skiptests"))
run_tests = 0;
+ else if (str_eq_lower(opt, "run"))
+ run_exe = 1;
else
printf("Unknown option ignored `%s`\n", argv[i]);
} else {
@@ -422,29 +588,16 @@ i32 main(i32 argc, c8 **argv) {
print_help();
return 0;
- case 't':
- build_type = argv[i + (++consumed)];
- break;
-
- case 'c':
- compiler_c = argv[i + (++consumed)];
- break;
-
- case 'd':
- destination = argv[i + (++consumed)];
- break;
-
- case 'o':
- extra_options = argv[i + (++consumed)];
- break;
-
- case 'l':
- extra_link_options = argv[i + (++consumed)];
- break;
-
- case 's':
- run_tests = 0;
- break;
+ case 't': build_type = argv[i + (++consumed)]; break;
+ case 'c': compiler_c = argv[i + (++consumed)]; break;
+ case 'd': destination = argv[i + (++consumed)]; break;
+ case 'o': extra_options = argv[i + (++consumed)]; break;
+ case 'l': extra_link_options = argv[i + (++consumed)]; break;
+ case 'e': rebuild_exe = 1; break;
+ case 'r': rebuild_all = 1; break;
+ case 'g': run_codegen = 1; break;
+ case 's': run_tests = 0; break;
+ case 'x': run_exe = 1; break;
default:
printf("Unknown option ignored `-%c`\n", argv[i][j]);
@@ -452,9 +605,8 @@ i32 main(i32 argc, c8 **argv) {
}
i += consumed;
}
- } else {
+ } else
printf("Unknown option ignored `%s`\n", argv[i]);
- }
}
fflush(stdout);
@@ -629,9 +781,9 @@ i32 main(i32 argc, c8 **argv) {
"";
if (extra_options[0] != '\0')
- flags = fmt_dup("%s %s", extra_options, flags);
+ flags = fmt_dup("%s %s", flags, extra_options);
if (extra_link_options[0] != '\0')
- link_flags = fmt_dup("%s %s", extra_link_options, link_flags);
+ link_flags = fmt_dup("%s %s", link_flags, extra_link_options);
// Prepare destination folder
//
@@ -639,82 +791,43 @@ i32 main(i32 argc, c8 **argv) {
destination[0] == '\0' && (destination = "build");
create_folder_recursive(destination);
- // Print info
- //
-
- printf("\nCompiler options: %s\n", flags);
- printf( "Link options: %s\n\n", link_flags);
- fflush(stdout);
-
- // Code generation
+ // Check timestamps
//
- // TODO Add command line option for code generation.
-
- if (!file_exists("build_fonts.inl.h")) {
- printf("Code generation\n");
- fflush(stdout);
-
- FILE *out = fopen("build_fonts.inl.h", "wb");
- assert(out != NULL);
-
- fprintf(out, "// "
- "====================================================="
- "===========\n");
- fprintf(out, "//\n");
- fprintf(out, "// Saw generated code\n");
- fprintf(out, "//\n");
- fprintf(out, "// "
- "====================================================="
- "===========\n\n");
-
- fprintf(out, "#ifndef BUILD_FONTS_INL_H\n");
- fprintf(out, "#define BUILD_FONTS_INL_H\n\n");
- fprintf(out, "#include \"kit/types.h\"\n\n");
-
- // Write Domitian Roman
- //
- {
- FILE *in = fopen("fonts/domitian_roman.ttf", "rb");
- assert(in != NULL);
- fprintf(out, "u32 ttf_text[] = {");
+ Timestamp time_font_text = file_timestamp(FONT_TEXT);
+ Timestamp time_font_icons = file_timestamp(FONT_ICONS);
+ Timestamp time_fonts = file_timestamp(INL_FONTS);
+ Timestamp time_source = file_timestamp(SOURCE);
+ Timestamp time_tests = file_timestamp(fmt("%s" DLM "test_suite", destination));
+ Timestamp time_exe = file_timestamp(fmt("%s" DLM PROJECT, destination));
- i64 n = print_bytes(out, in);
+ if (time_less(time_fonts, time_font_text) ||
+ time_less(time_fonts, time_font_icons))
+ run_codegen = 1;
- fprintf(out, "\n};\n\n");
- fprintf(out, "enum { TTF_TEXT_SIZE = %lld, };\n\n", n);
-
- fclose(in);
- }
-
- // Write Font Awesome
- //
- {
- FILE *in = fopen("fonts/font_awesome_6_free_solid_900.ttf", "rb");
- assert(in != NULL);
-
- fprintf(out, "u32 ttf_icons[] = {");
-
- i64 n = print_bytes(out, in);
+ if (!file_exists(fmt("%s" DLM "thirdparty%s", destination, postfix_obj))) {
+ rebuild_all = 1;
+ rebuild_exe = 1;
+ }
- fprintf(out, "\n};\n\n");
- fprintf(out, "enum { TTF_ICONS_SIZE = %lld, };\n\n", n);
+ if (time_less(time_tests, time_source) ||
+ time_less(time_exe, time_source))
+ rebuild_exe = 1;
- fclose(in);
- }
+ // Print info
+ //
- fprintf(out, "#endif\n");
- fclose(out);
- }
+ printf("\nCompiler options: %s\n", flags);
+ printf( "Link options: %s\n\n", link_flags);
+ fflush(stdout);
// Build the project
//
- c8 *deps = fmt("%s" DLM "thirdparty%s", destination, postfix_obj);
-
- if (!file_exists(deps)) {
- // TODO Add command line option for complete rebuild.
+ if (run_codegen)
+ codegen();
+ if (rebuild_all) {
printf("Rebuild dependencies\n");
fflush(stdout);
@@ -733,65 +846,59 @@ i32 main(i32 argc, c8 **argv) {
fflush(stdout);
}
- printf("Build the test suite\n");
- fflush(stdout);
-
- i32 s = system(fmt(
- "%s %s -DTESTS=1 "
- "%s%s" DLM "test_suite%s "
- SOURCE
- " %s",
- compiler_c, flags,
- flag_exe, destination, postfix_exe,
- link_flags)
- );
-
- if (WEXITSTATUS(s) != 0)
- return 1;
- fflush(stdout);
-
- printf("Build " PROJECT " executable\n");
- fflush(stdout);
+ if (rebuild_exe) {
+ printf("Build the test suite\n");
+ fflush(stdout);
- s = system(fmt(
- "%s %s -DEXE=1 "
- "%s%s" DLM PROJECT "%s "
- "%s" DLM "thirdparty%s "
- SOURCE
- " %s",
- compiler_c, flags,
- flag_exe, destination, postfix_exe,
- destination, postfix_obj,
- link_flags)
- );
+ i32 s = system(fmt(
+ "%s %s -DTESTS=1 "
+ "%s%s" DLM "test_suite%s "
+ SOURCE
+ " %s",
+ compiler_c, flags,
+ flag_exe, destination, postfix_exe,
+ link_flags)
+ );
- if (WEXITSTATUS(s) != 0)
- return 1;
- fflush(stdout);
+ if (WEXITSTATUS(s) != 0)
+ return 1;
+ fflush(stdout);
- if (!run_tests)
- return 0;
+ printf("Build " PROJECT " executable\n");
+ fflush(stdout);
- // Run tests
- //
+ s = system(fmt(
+ "%s %s -DEXE=1 "
+ "%s%s" DLM PROJECT "%s "
+ "%s" DLM "thirdparty%s "
+ SOURCE
+ " %s",
+ compiler_c, flags,
+ flag_exe, destination, postfix_exe,
+ destination, postfix_obj,
+ link_flags)
+ );
- i32 status = 0;
+ if (WEXITSTATUS(s) != 0)
+ return 1;
+ fflush(stdout);
+ }
- printf("Run tests\n\n");
- fflush(stdout);
+ if (run_tests) {
+ printf("Run tests\n");
+ fflush(stdout);
- s = system(fmt("\"%s" DLM "test_suite\"", destination));
+ i32 s = system(fmt("\"%s" DLM "test_suite\"", destination));
- if (WEXITSTATUS(s) != 0)
- status = 1;
- fflush(stdout);
+ if (WEXITSTATUS(s) != 0)
+ return -1;
+ }
- if (status == 0)
- printf("\nAll done - OK.\n");
- else
- printf("\nAll done - FAILURE.\n");
+ if (run_exe)
+ system(fmt("\"%s" DLM PROJECT "\"", destination));
- return status;
+ printf("All done\n");
+ return 0;
}
// ================================================================
@@ -882,7 +989,7 @@ i32 main(i32 argc, c8 **argv) {
// ttf_text, TTF_TEXT_SIZE
// ttf_icons, TTF_ICONS_SIZE
-#include "build_fonts.inl.h"
+#include "build_fonts.inl.c"
// ================================================================
//
@@ -4724,5 +4831,5 @@ i32 main(i32 argc, char **argv) {
// ================================================================
#else
-#error Build options not provided. Try: gcc -DBUILDME saw.c
+#error Build options not provided. Try: gcc -DBUILDME=1 saw.c
#endif