diff --git a/flake.nix b/flake.nix index a243f07..e870ace 100644 --- a/flake.nix +++ b/flake.nix @@ -59,10 +59,7 @@ forAllSystemsWithPkgs [(import rust-overlay)] ({ system, pkgs, - }: - let - in - { + }: { packages.${system} = let rust = { nativeBuildInputs = [ @@ -88,6 +85,7 @@ pg-from = pkgs.callPackage ./package/postgres/pg-from/default.nix rust.commonArgs; pg-schema = pkgs.callPackage ./package/postgres/pg-schema/default.nix rust.commonArgs; pg-migration = pkgs.callPackage ./package/postgres/pg-migration/default.nix rust.commonArgs; + libhectic = pkgs.callPackage ./package/c/libhectic/default.nix {}; }; devShells.${system} = let @@ -129,6 +127,89 @@ buildInputs = [pkgs.stack]; }); }; + nixosConfigurations."${system}_manual_test" = nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + self.nixosModules."preset.default" + self.nixosModules."hardware.hetzner" + ({modulesPath, pkgs, ...}: { + imports = [ + (modulesPath + "/profiles/qemu-guest.nix") + ]; + + users.users.root.openssh.authorizedKeys.keys = [ ]; + environment.systemPackages = with pkgs; [ + (pkgs.writers.writeMinCBin "minc-hello-world" [""] /*c*/ '' + printf("hello world\n"); + '') + (pkgs.writers.writeMinCBin "minc-env" ["" ""] /*c*/ '' + char *env_name; + if (argc > 1) { + env_name = argv[1]; + } else { + env_name = "HOME"; + } + + char *value = getenv(env_name); + if (value) { + printf("%s: %s\n", env_name, value); + } else { + printf("Environment variable %s not found.\n", env_name); + } + '') + (pkgs.writers.writeMinCBin "minc-env-check" ["" ""] /*c*/ '' + char *env_name; + if (argc > 1) { + env_name = argv[1]; + } else { + env_name = "HOME"; + } + + char *value = getenv(env_name); + if (value) { + char buffer[128]; + sprintf(buffer, "echo $%s\n", env_name); + system(buffer); + } else { + printf("Environment variable %s not found.\n", env_name); + } + '') + ]; + programs.zsh.shellAliases = { + jc = ''journalctl''; + sc = ''journalctl''; + nv = ''nvim''; + sd = "shutdown now"; + }; + + virtualisation = { + vmVariant = { + systemd.services.fix-root-perms = { + description = "Fix root directory permissions"; + after = [ "local-fs.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.coreutils}/bin/chmod 755 /"; + }; + }; + virtualisation = { + diskSize = 1024*6; + diskImage = null; + forwardPorts = [ ]; + }; + }; + }; + networking.firewall = { + enable = true; + allowedTCPPorts = [ + 80 + ]; + }; + }) + ]; + pkgs = import nixpkgs {inherit system; overlays = [ self.overlays.default ];}; + }; }) // { nixosModules = { diff --git a/package/c/lib/Makefile b/package/c/lib/Makefile deleted file mode 100644 index 6d0c6c8..0000000 --- a/package/c/lib/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -CC = gcc -CFLAGS = -Wall -Wextra -std=c11 -LDFLAGS = -L. -lmylib # Link with your library - -# Source files -LIB_SRC = mylib.c -LIB_OBJ = $(LIB_SRC:.c=.o) -TEST_SRC = test/test.c -TEST_OBJ = $(TEST_SRC:.c=.o) -TEST_BIN = test - -# Build library -libmylib.a: $(LIB_OBJ) - ar rcs $@ $^ - -# Build test executable -$(TEST_BIN): $(TEST_OBJ) libmylib.a - $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) - -# Compile C files -%.o: %.c - $(CC) $(CFLAGS) -c $< -o $@ - -# Clean build artifacts -clean: - rm -f $(LIB_OBJ) $(TEST_OBJ) $(TEST_BIN) libmylib.a diff --git a/package/c/lib/default.nix b/package/c/lib/default.nix deleted file mode 100644 index e69de29..0000000 diff --git a/package/c/lib/lib/heclib.c b/package/c/lib/lib/heclib.c deleted file mode 100644 index e69de29..0000000 diff --git a/package/c/lib/test/test.c b/package/c/lib/test/test.c deleted file mode 100644 index b31e5c3..0000000 --- a/package/c/lib/test/test.c +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include -#include "heclib.h" - -void test_function() { -} - -int main(void) { - test_function(); - printf("All tests passed\n"); - return 0; -} diff --git a/package/c/libhectic/default.nix b/package/c/libhectic/default.nix new file mode 100644 index 0000000..39f825f --- /dev/null +++ b/package/c/libhectic/default.nix @@ -0,0 +1,34 @@ +{ stdenv, gcc, lib }: + +stdenv.mkDerivation { + pname = "libhectic"; + version = "1.0"; + src = ./.; + doCheck = true; + + buildPhase = '' + mkdir -p target + ${gcc}/bin/cc -Wall -Wextra -g -pedantic -fsanitize=address -c libhectic.c -o target/libhectic.o + ${gcc}/bin/ar rcs target/libhectic.a target/libhectic.o + ''; + + checkPhase = '' + mkdir -p target/test + for test_file in test/*.c; do + exe="target/test/$(basename ''${test_file%.c})" + ${gcc}/bin/cc -Wall -Wextra -g -pedantic -fsanitize=address -I. "$test_file" -Ltarget -l:libhectic.a -o "$exe" + "$exe" + done + ''; + + installPhase = '' + mkdir -p $out/lib $out/include + cp target/libhectic.a $out/lib/ + cp libhectic.h $out/include/ + ''; + + meta = { + description = "libhectic"; + license = lib.licenses.mit; + }; +} diff --git a/package/c/libhectic/libhectic.c b/package/c/libhectic/libhectic.c new file mode 100644 index 0000000..1c17e3f --- /dev/null +++ b/package/c/libhectic/libhectic.c @@ -0,0 +1,76 @@ +#include "libhectic.h" + +void set_output_color_mode(ColorMode mode) { + color_mode = mode; +} + +// ------------ +// -- Logger -- +// ------------ + +const char* log_level_to_string(LogLevel level) { + switch (level) { + case LOG_LEVEL_DEBUG: return "DEBUG"; + case LOG_LEVEL_LOG: return "LOG"; + case LOG_LEVEL_INFO: return "INFO"; + case LOG_LEVEL_NOTICE: return "NOTICE"; + case LOG_LEVEL_WARN: return "WARN"; + case LOG_LEVEL_EXCEPTION: return "EXCEPTION"; + default: return "UNKNOWN"; + } +} + +LogLevel log_level_from_string(const char *level_str) { + if (!level_str) return LOG_LEVEL_INFO; + if (strcmp(level_str, "DEBUG") == 0) + return LOG_LEVEL_DEBUG; + else if (strcmp(level_str, "LOG") == 0) + return LOG_LEVEL_LOG; + else if (strcmp(level_str, "INFO") == 0) + return LOG_LEVEL_INFO; + else if (strcmp(level_str, "NOTICE") == 0) + return LOG_LEVEL_NOTICE; + else if (strcmp(level_str, "WARN") == 0) + return LOG_LEVEL_WARN; + else if (strcmp(level_str, "EXCEPTION") == 0) + return LOG_LEVEL_EXCEPTION; + else + return LOG_LEVEL_INFO; +} + +LogLevel current_log_level = LOG_LEVEL_INFO; + +void logger_level_reset() { + current_log_level = LOG_LEVEL_INFO; +} + +void logger_level(LogLevel level) { + current_log_level = level; +} + +void init_logger(void) { + current_log_level = log_level_from_string(getenv("LOG_LEVEL")); +} + +char* log_message(LogLevel level, int line, const char *format, ...) { + if (level < current_log_level) { + return NULL; + } + + time_t now = time(NULL); + struct tm tm_info; + localtime_r(&now, &tm_info); + static char timeStr[20]; + strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &tm_info); + + fprintf(stderr, "%s %d %s: ", timeStr, line, log_level_to_string(level)); + + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + + fprintf(stderr, "\n"); + + return timeStr; +} diff --git a/package/c/lib/lib/heclib.h b/package/c/libhectic/libhectic.h similarity index 65% rename from package/c/lib/lib/heclib.h rename to package/c/libhectic/libhectic.h index 5d3e78e..c3f7734 100644 --- a/package/c/lib/lib/heclib.h +++ b/package/c/libhectic/libhectic.h @@ -37,9 +37,7 @@ typedef enum { static ColorMode color_mode = COLOR_MODE_AUTO; // Function to set color mode -void set_output_color_mode(ColorMode mode) { - color_mode = mode; -} +void set_output_color_mode(ColorMode mode); // Macros for detecting terminal and color usage #define IS_TERMINAL() (isatty(fileno(stderr))) @@ -53,8 +51,10 @@ void set_output_color_mode(ColorMode mode) { // ------------ // Define color macros based on output type -#define ERROR_PREFIX PP_CAT_I(COLOR_RED, "Error: ") -#define ERROR_SUFFIX PP_CAT_I(COLOR_RESET, "\n") +//#define ERROR_PREFIX PP_CAT_I(COLOR_RED, "Error: ") +//#define ERROR_SUFFIX PP_CAT_I(COLOR_RESET, "\n") +#define ERROR_PREFIX (USE_COLOR() ? "\033[1;31mError: " : "Error: ") +#define ERROR_SUFFIX (USE_COLOR() ? "\033[0m\n" : "\n") // eprintf handling 1 or more arguments #define eprintf_1(fmt) \ @@ -81,62 +81,11 @@ typedef enum { LOG_LEVEL_EXCEPTION } LogLevel; -const char* log_level_to_string(LogLevel level) { - switch (level) { - case LOG_LEVEL_DEBUG: return "DEBUG"; - case LOG_LEVEL_LOG: return "LOG"; - case LOG_LEVEL_INFO: return "INFO"; - case LOG_LEVEL_NOTICE: return "NOTICE"; - case LOG_LEVEL_WARN: return "WARN"; - case LOG_LEVEL_EXCEPTION: return "EXCEPTION"; - default: return "UNKNOWN"; - } -} +void logger_level(LogLevel level); -LogLevel log_level_from_string(const char *level_str) { - if (!level_str) return LOG_LEVEL_INFO; - if (strcmp(level_str, "DEBUG") == 0) - return LOG_LEVEL_DEBUG; - else if (strcmp(level_str, "LOG") == 0) - return LOG_LEVEL_LOG; - else if (strcmp(level_str, "INFO") == 0) - return LOG_LEVEL_INFO; - else if (strcmp(level_str, "NOTICE") == 0) - return LOG_LEVEL_NOTICE; - else if (strcmp(level_str, "WARN") == 0) - return LOG_LEVEL_WARN; - else if (strcmp(level_str, "EXCEPTION") == 0) - return LOG_LEVEL_EXCEPTION; - else - return LOG_LEVEL_INFO; -} +LogLevel log_level_from_string(const char *level_str); -LogLevel current_log_level = LOG_LEVEL_INFO; - -void init_logger(void) { - current_log_level = log_level_from_string(getenv("LOG_LEVEL")); -} - -void log_message(LogLevel level, int line, const char *format, ...) { - if (level < current_log_level) { - return; - } - - time_t now = time(NULL); - struct tm tm_info; - localtime_r(&now, &tm_info); - char timeStr[20]; - strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &tm_info); - - fprintf(stderr, "%s %d %s: ", timeStr, line, log_level_to_string(level)); - - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); - - fprintf(stderr, "\n"); -} +char* log_message(LogLevel level, int line, const char *format, ...); // DEBUG level #define raise_debug_1(fmt) \ diff --git a/package/c/libhectic/test/test.c b/package/c/libhectic/test/test.c new file mode 100644 index 0000000..8dba2aa --- /dev/null +++ b/package/c/libhectic/test/test.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include "libhectic.h" + +// Reusable test function for any raise_* logging function. +// The logging function must have the signature: char* func(const char* message) +void test_raise_generic(const char* (*raise_func)(const char*), LogLevel log_level, const char* level_str) { + FILE *orig_stderr = stderr; + FILE *temp = tmpfile(); + char result_buffer[256]; + + if (!temp) { + perror("tmpfile"); + exit(EXIT_FAILURE); + } + stderr = temp; + + logger_level(log_level); + int line = __LINE__; + char* time_str = raise_func("message"); + logger_level_reset(); + + fflush(stderr); + fseek(temp, 0, SEEK_SET); + size_t nread = fread(result_buffer, 1, sizeof(result_buffer) - 1, temp); + result_buffer[nread] = '\0'; + + stderr = orig_stderr; + fclose(temp); + + char expected_buffer[256]; + sprintf(expected_buffer, "%s %d %s: message\n", time_str, line + 1, level_str, message); + assert(strcmp(result_buffer, expected_buffer) == 0); +} + +int main(void) { + set_output_color_mode(COLOR_MODE_DISABLE); + + test_raise_generic(raise_log, LOG_LEVEL_LOG, "LOG"); + test_raise_generic(raise_debug, LOG_LEVEL_DEBUG, "DEBUG"); + test_raise_generic(raise_warn, LOG_LEVEL_INFO, "INFO"); + test_raise_generic(raise_notice, LOG_LEVEL_NOTICE, "NOTICE"); + test_raise_generic(raise_warn, LOG_LEVEL_WARN, "WARN"); + test_raise_generic(raise_exception, LOG_LEVEL_EXCEPTION, "EXCEPTION"); + + return 0; +}