Add unit tests + fix allocator
This commit is contained in:
parent
6c6f7eb902
commit
d86948b6a9
4 changed files with 495 additions and 16 deletions
254
axl_koan.h
Normal file
254
axl_koan.h
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* koan.h — minimalistic unit-testing library
|
||||
* Author: NukeBird, 2025
|
||||
* License: public domain / MIT — as you wish
|
||||
*/
|
||||
#ifndef KOAN_H
|
||||
#define KOAN_H
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#define koan_isatty(_f) _isatty(_fileno(_f))
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define koan_isatty(_f) isatty(fileno(_f))
|
||||
#endif
|
||||
|
||||
struct koan_test
|
||||
{
|
||||
const char* name;
|
||||
void (*func)(jmp_buf);
|
||||
struct koan_test* next;
|
||||
};
|
||||
|
||||
static struct koan_test* koan_tests = NULL;
|
||||
static struct koan_test** koan_tail = &koan_tests;
|
||||
|
||||
static void koan_add_test(const char* name, void (*func)(jmp_buf))
|
||||
{
|
||||
struct koan_test* test = malloc(sizeof *test);
|
||||
if (!test) {
|
||||
fprintf(stderr, "koan: out of memory for test registration\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
test->name = name;
|
||||
test->func = func;
|
||||
test->next = NULL;
|
||||
*koan_tail = test;
|
||||
koan_tail = &test->next;
|
||||
}
|
||||
|
||||
static char* koan_fail_msg = NULL;
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Assertion macros */
|
||||
/* ------------------------------------------------------------------ */
|
||||
#define KOAN_FAIL(reason) \
|
||||
do { \
|
||||
size_t _sz = strlen(reason) + strlen(__FILE__) + 128; \
|
||||
char* _buf = malloc(_sz); \
|
||||
if (_buf) { \
|
||||
snprintf(_buf, _sz, "%s\n at %s:%d", reason, __FILE__, __LINE__); \
|
||||
} else { \
|
||||
_buf = "koan: malloc failed while formatting error message"; \
|
||||
} \
|
||||
koan_fail_msg = _buf; \
|
||||
longjmp(koan_jmp_buf, 1); \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_TRUE(cond) \
|
||||
do { if (!(cond)) { char _r[256]; snprintf(_r, sizeof(_r), "ASSERT_TRUE failed: (%s) is false", #cond); KOAN_FAIL(_r); } } while (0)
|
||||
|
||||
#define ASSERT_FALSE(cond) \
|
||||
do { if (cond) { char _r[256]; snprintf(_r, sizeof(_r), "ASSERT_FALSE failed: (%s) is true", #cond); KOAN_FAIL(_r); } } while (0)
|
||||
|
||||
#define ASSERT_INT_EQ(expected, actual) \
|
||||
do { \
|
||||
int64_t _e = (int64_t)(expected); \
|
||||
int64_t _a = (int64_t)(actual); \
|
||||
if (_e != _a) { \
|
||||
char _r[256]; \
|
||||
snprintf(_r, sizeof(_r), "ASSERT_INT_EQ failed: expected %" PRId64 " but got %" PRId64, _e, _a); \
|
||||
KOAN_FAIL(_r); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_UINT_EQ(expected, actual) \
|
||||
do { \
|
||||
uint64_t _e = (uint64_t)(expected); \
|
||||
uint64_t _a = (uint64_t)(actual); \
|
||||
if (_e != _a) { \
|
||||
char _r[256]; \
|
||||
snprintf(_r, sizeof(_r), "ASSERT_UINT_EQ failed: expected %" PRIu64 " but got %" PRIu64, _e, _a); \
|
||||
KOAN_FAIL(_r); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_DOUBLE_EQ(expected, actual, eps) \
|
||||
do { \
|
||||
double _e = (double)(expected); \
|
||||
double _a = (double)(actual); \
|
||||
double _eps = (double)(eps); \
|
||||
if (fabs(_e - _a) > _eps) { \
|
||||
char _r[512]; \
|
||||
snprintf(_r, sizeof(_r), "ASSERT_DOUBLE_EQ failed: expected %g but got %g (diff %g > eps %g)", \
|
||||
_e, _a, fabs(_e - _a), _eps); \
|
||||
KOAN_FAIL(_r); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_STR_EQ(expected, actual) \
|
||||
do { \
|
||||
const char* _e = (expected); \
|
||||
const char* _a = (actual); \
|
||||
if ((_e == NULL || _a == NULL) ? (_e != _a) : strcmp(_e, _a)) { \
|
||||
char _r[512]; \
|
||||
snprintf(_r, sizeof(_r), "ASSERT_STR_EQ failed: expected \"%s\" but got \"%s\"", \
|
||||
_e ? _e : "(null)", _a ? _a : "(null)"); \
|
||||
KOAN_FAIL(_r); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_PTR_EQ(expected, actual) \
|
||||
do { \
|
||||
const void* _e = (const void*)(expected); \
|
||||
const void* _a = (const void*)(actual); \
|
||||
if (_e != _a) { \
|
||||
char _r[256]; \
|
||||
snprintf(_r, sizeof(_r), "ASSERT_PTR_EQ failed: expected %p but got %p", _e, _a); \
|
||||
KOAN_FAIL(_r); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_NULL(ptr) ASSERT_PTR_EQ(NULL, (ptr))
|
||||
#define ASSERT_NOT_NULL(ptr) ASSERT_TRUE((ptr) != NULL)
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Koan definition */
|
||||
/* ------------------------------------------------------------------ */
|
||||
#define KOAN(name) \
|
||||
static void koan_test_##name(jmp_buf); \
|
||||
__attribute__((constructor)) static void koan_register_##name(void) \
|
||||
{ koan_add_test(#name, koan_test_##name); } \
|
||||
static void koan_test_##name(jmp_buf koan_jmp_buf)
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Time formatting */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void koan_format_time(double secs, char* buf, size_t bufsz)
|
||||
{
|
||||
double ns = secs * 1e9;
|
||||
if (ns < 1e3) snprintf(buf, bufsz, "%.0f ns", ns);
|
||||
else if (ns < 1e6) snprintf(buf, bufsz, "%.2f µs", ns / 1e3);
|
||||
else if (ns < 1e9) snprintf(buf, bufsz, "%.2f ms", ns / 1e6);
|
||||
else snprintf(buf, bufsz, "%.3f s", secs);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Progress bar (final only) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void koan_print_bar(int done, int total, int passed, size_t failed, double elapsed)
|
||||
{
|
||||
const int w = 40;
|
||||
int pos = (int)((float)done * w / total);
|
||||
printf("[");
|
||||
for (int i = 0; i < w; ++i) putchar(i < pos ? '=' : (i == pos ? '>' : '-'));
|
||||
printf("] %3d%% %d/%d passed:%d failed:%zu %.3fs\n",
|
||||
(int)((float)done * 100 / total), done, total, passed, failed, elapsed);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Run all koans — simple sequential mode only */
|
||||
/* ------------------------------------------------------------------ */
|
||||
struct koan_failure { const char* name; const char* msg; };
|
||||
|
||||
int koan_run_all(void)
|
||||
{
|
||||
printf("\n");
|
||||
int total = 0;
|
||||
for (struct koan_test* t = koan_tests; t; t = t->next) total++;
|
||||
|
||||
if (total == 0) {
|
||||
printf("koan: no koans — no enlightenment.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int colorful = koan_isatty(stdout);
|
||||
const char* G = colorful ? "\033[32m" : "";
|
||||
const char* R = colorful ? "\033[31m" : "";
|
||||
const char* Y = colorful ? "\033[33m" : "";
|
||||
const char* N = colorful ? "\033[0m" : "";
|
||||
|
||||
const char* OK = colorful ? "PASS " : "[PASS] ";
|
||||
const char* FAIL = colorful ? "FAIL " : "[FAIL] ";
|
||||
|
||||
printf("%sEnlightening %d %s...%s\n\n", Y, total, total == 1 ? "koan" : "koans", N);
|
||||
|
||||
struct koan_failure* failures = NULL;
|
||||
size_t fail_cap = 0, fail_cnt = 0;
|
||||
int passed = 0;
|
||||
clock_t start = clock();
|
||||
|
||||
for (struct koan_test* t = koan_tests; t; t = t->next) {
|
||||
koan_fail_msg = NULL;
|
||||
clock_t ts = clock();
|
||||
jmp_buf env;
|
||||
int result = setjmp(env);
|
||||
if (result == 0) {
|
||||
t->func(env);
|
||||
double sec = (double)(clock() - ts) / CLOCKS_PER_SEC;
|
||||
char tbuf[32];
|
||||
koan_format_time(sec, tbuf, sizeof tbuf);
|
||||
printf(" %s%s%s (%s)%s\n", G, OK, t->name, tbuf, N);
|
||||
passed++;
|
||||
} else {
|
||||
double sec = (double)(clock() - ts) / CLOCKS_PER_SEC;
|
||||
char tbuf[32];
|
||||
koan_format_time(sec, tbuf, sizeof tbuf);
|
||||
printf(" %s%s%s (%s)%s\n", R, FAIL, t->name, tbuf, N);
|
||||
|
||||
if (fail_cnt == fail_cap) {
|
||||
fail_cap = fail_cap ? fail_cap * 2 : 16;
|
||||
failures = realloc(failures, fail_cap * sizeof *failures);
|
||||
if (!failures) exit(1);
|
||||
}
|
||||
failures[fail_cnt].name = t->name;
|
||||
failures[fail_cnt].msg = koan_fail_msg ? koan_fail_msg : "unknown failure";
|
||||
fail_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
double total_time = (double)(clock() - start) / CLOCKS_PER_SEC;
|
||||
koan_print_bar(total, total, passed, fail_cnt, total_time);
|
||||
printf("\n");
|
||||
|
||||
if (fail_cnt == 0) {
|
||||
printf("%sAll %d %s achieved enlightenment in %.3f s!%s\n", G, total,
|
||||
total == 1 ? "koan" : "koans", total_time, N);
|
||||
free(failures);
|
||||
return 0;
|
||||
} else {
|
||||
printf("%s%zu / %d %s did not achieve enlightenment (%.3f s)%s\n\n", R,
|
||||
fail_cnt, total, fail_cnt == 1 ? "koan" : "koans", total_time, N);
|
||||
printf("%s--- Failed Koans ---%s\n\n", R, N);
|
||||
for (size_t i = 0; i < fail_cnt; ++i) {
|
||||
printf(" %sKoan \"%s\" failed%s\n", R, failures[i].name, N);
|
||||
printf(" %s\n\n", failures[i].msg);
|
||||
free((void*)failures[i].msg);
|
||||
}
|
||||
free(failures);
|
||||
printf("%sEnlightenment not achieved.%s\n", R, N);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* KOAN_H */
|
||||
20
axl_memory.c
20
axl_memory.c
|
|
@ -45,7 +45,7 @@ static mb_header* axl_id_to_mb(mb_handle id)
|
|||
return (mb_header*)&memory[id];
|
||||
}
|
||||
|
||||
static mb_handle axl_ptr_to_id(mb_header* ptr)
|
||||
static mb_handle axl_mb_to_id(mb_header* ptr)
|
||||
{
|
||||
return (mb_handle)((u8*)ptr - memory);
|
||||
}
|
||||
|
|
@ -126,15 +126,15 @@ static b8 axl_split_mb(mb_header* block, u32 size)
|
|||
new_block->size = block->size - size - MB_HEADER_SIZE;
|
||||
new_block->is_free = true;
|
||||
new_block->next = block->next;
|
||||
new_block->prev = axl_ptr_to_id(block);
|
||||
new_block->prev = axl_mb_to_id(block);
|
||||
|
||||
if(new_block->next)
|
||||
if(new_block->next != AXL_INVALID_MB_HANDLE)
|
||||
{
|
||||
axl_id_to_mb(new_block->next)->prev = axl_ptr_to_id(new_block);
|
||||
axl_id_to_mb(new_block->next)->prev = axl_mb_to_id(new_block);
|
||||
}
|
||||
|
||||
block->size = size;
|
||||
block->next = axl_ptr_to_id(new_block);
|
||||
block->next = axl_mb_to_id(new_block);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -256,9 +256,9 @@ void axl_free(void* ptr)
|
|||
{
|
||||
block->size += next_block->size + MB_HEADER_SIZE;
|
||||
block->next = next_block->next;
|
||||
if(block->next)
|
||||
if(block->next != AXL_INVALID_MB_HANDLE)
|
||||
{
|
||||
axl_id_to_mb(block->next)->prev = axl_ptr_to_id(block);
|
||||
axl_id_to_mb(block->next)->prev = axl_mb_to_id(block);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,12 +266,12 @@ void axl_free(void* ptr)
|
|||
{
|
||||
prev_block->size += block->size + MB_HEADER_SIZE;
|
||||
prev_block->next = block->next;
|
||||
if(prev_block->next)
|
||||
if(prev_block->next != AXL_INVALID_MB_HANDLE)
|
||||
{
|
||||
axl_id_to_mb(prev_block->next)->prev = axl_ptr_to_id(prev_block);
|
||||
axl_id_to_mb(prev_block->next)->prev = axl_mb_to_id(prev_block);
|
||||
}
|
||||
free_block = prev_block;
|
||||
}
|
||||
|
||||
nomad_handle = axl_ptr_to_id(free_block);
|
||||
nomad_handle = axl_mb_to_id(free_block);
|
||||
}
|
||||
|
|
|
|||
212
axl_memory_test.c
Normal file
212
axl_memory_test.c
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* axl_memory_tests.c — Comprehensive unit tests for axl_memory.h
|
||||
*/
|
||||
|
||||
#include "axl_memory.h"
|
||||
#include "axl_koan.h"
|
||||
#include <string.h>
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Basic allocation & initialization */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
KOAN(init_called)
|
||||
{
|
||||
void* p = axl_malloc(1);
|
||||
ASSERT_NOT_NULL(p);
|
||||
axl_free(p);
|
||||
}
|
||||
|
||||
KOAN(malloc_returns_different_pointers)
|
||||
{
|
||||
void* a = axl_malloc(16);
|
||||
void* b = axl_malloc(32);
|
||||
ASSERT_NOT_NULL(a);
|
||||
ASSERT_NOT_NULL(b);
|
||||
ASSERT_TRUE(a != b);
|
||||
axl_free(a);
|
||||
axl_free(b);
|
||||
}
|
||||
|
||||
KOAN(malloc_exhaustion_returns_null)
|
||||
{
|
||||
void* p = axl_malloc(AXL_HEAP_SIZE + 4096); /* definitely too big */
|
||||
ASSERT_NULL(p);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Realloc behavior (strictly per C standard) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
KOAN(realloc_null_acts_like_malloc)
|
||||
{
|
||||
void* p = axl_realloc(NULL, 64);
|
||||
ASSERT_NOT_NULL(p);
|
||||
axl_memset(p, 0x55, 64);
|
||||
axl_free(p);
|
||||
}
|
||||
|
||||
KOAN(realloc_zero_size_frees_and_returns_null_or_valid)
|
||||
{
|
||||
void* p = axl_malloc(100);
|
||||
ASSERT_NOT_NULL(p);
|
||||
void* q = axl_realloc(p, 0);
|
||||
if (q != NULL) axl_free(q);
|
||||
/* Original p is freed regardless */
|
||||
}
|
||||
|
||||
KOAN(realloc_enlarge_preserves_content)
|
||||
{
|
||||
void* p = axl_malloc(32);
|
||||
axl_memset(p, 0xCD, 32);
|
||||
void* q = axl_realloc(p, 128);
|
||||
ASSERT_NOT_NULL(q);
|
||||
for (int i = 0; i < 32; i++)
|
||||
ASSERT_INT_EQ(0xCD, ((u8*)q)[i]);
|
||||
axl_free(q);
|
||||
}
|
||||
|
||||
KOAN(realloc_shrink_preserves_prefix)
|
||||
{
|
||||
void* p = axl_malloc(256);
|
||||
axl_memset(p, 0xAB, 256);
|
||||
void* q = axl_realloc(p, 64);
|
||||
ASSERT_NOT_NULL(q);
|
||||
for (int i = 0; i < 64; i++)
|
||||
ASSERT_INT_EQ(0xAB, ((u8*)q)[i]);
|
||||
axl_free(q);
|
||||
}
|
||||
|
||||
KOAN(realloc_same_size_returns_same_or_equivalent)
|
||||
{
|
||||
void* p = axl_malloc(100);
|
||||
|
||||
p = axl_realloc(p, 100);
|
||||
ASSERT_NOT_NULL(p);
|
||||
/* May return same or different pointer */
|
||||
axl_free(p);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Memory operations */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
KOAN(memset_fills_correctly)
|
||||
{
|
||||
void* p = axl_malloc(64);
|
||||
axl_memset(p, 0x55, 64);
|
||||
for (int i = 0; i < 64; i++)
|
||||
ASSERT_INT_EQ(0x55, ((u8*)p)[i]);
|
||||
axl_free(p);
|
||||
}
|
||||
|
||||
KOAN(memset_zero_bytes_is_nop)
|
||||
{
|
||||
void* p = axl_malloc(16);
|
||||
axl_memset(p, 0xFF, 16);
|
||||
axl_memset(p, 0x00, 0); /* should not change anything */
|
||||
for (int i = 0; i < 16; i++)
|
||||
ASSERT_INT_EQ(0xFF, ((u8*)p)[i]);
|
||||
axl_free(p);
|
||||
}
|
||||
|
||||
KOAN(memcpy_copies_exact_bytes)
|
||||
{
|
||||
u8 src[32];
|
||||
for (int i = 0; i < 32; i++) src[i] = (u8)i;
|
||||
void* dst = axl_malloc(32);
|
||||
axl_memcpy(dst, src, 32);
|
||||
ASSERT_INT_EQ(0, memcmp(dst, src, 32));
|
||||
axl_free(dst);
|
||||
}
|
||||
|
||||
KOAN(memcpy_zero_bytes_is_nop)
|
||||
{
|
||||
u8 data[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
|
||||
void* p = axl_malloc(16);
|
||||
axl_memcpy(p, data, 16);
|
||||
axl_memcpy(p, "junk", 0); /* must not modify */
|
||||
ASSERT_INT_EQ(0, memcmp(p, data, 16));
|
||||
axl_free(p);
|
||||
}
|
||||
|
||||
KOAN(memcmp_returns_correct_sign)
|
||||
{
|
||||
u8 a[] = {0, 0, 0, 0};
|
||||
u8 b[] = {0, 0, 0, 1};
|
||||
u8 c[] = {0, 0, 1, 0};
|
||||
|
||||
ASSERT_INT_EQ(0, axl_memcmp(a, a, 4));
|
||||
ASSERT_TRUE(axl_memcmp(a, b, 4) < 0);
|
||||
ASSERT_TRUE(axl_memcmp(b, a, 4) > 0);
|
||||
ASSERT_TRUE(axl_memcmp(a, c, 3) < 0 );
|
||||
ASSERT_TRUE(axl_memcmp(a, b, 2) == 0);
|
||||
ASSERT_TRUE(axl_memcmp(a, c, 2) == 0);
|
||||
ASSERT_TRUE(axl_memcmp(c, b, 2) == 0);
|
||||
ASSERT_TRUE(axl_memcmp(a, c, 4) < 0);
|
||||
}
|
||||
|
||||
KOAN(memcmp_zero_length_returns_zero)
|
||||
{
|
||||
u8 x = 0xFF, y = 0x00;
|
||||
ASSERT_INT_EQ(0, axl_memcmp(&x, &y, 0));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Double-free and use-after-free detection (indirect) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
KOAN(double_free_does_not_crash)
|
||||
{
|
||||
void* p = axl_malloc(64);
|
||||
ASSERT_NOT_NULL(p);
|
||||
axl_free(p);
|
||||
axl_free(p); /* should be ignored or handled safely */
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Large and boundary allocations */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
KOAN(large_allocation_near_heap_limit)
|
||||
{
|
||||
/* Allocate almost entire heap */
|
||||
void* p = axl_malloc(AXL_HEAP_SIZE - 1024);
|
||||
ASSERT_NOT_NULL(p); /* may pass if overhead is small */
|
||||
if (p) axl_free(p);
|
||||
}
|
||||
|
||||
KOAN(allocation_of_max_size_fails)
|
||||
{
|
||||
void* p = axl_malloc(AXL_HEAP_SIZE);
|
||||
ASSERT_NULL(p);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Stress: many small allocations */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
KOAN(many_small_allocations)
|
||||
{
|
||||
#define N 1000
|
||||
void* ptrs[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
ptrs[i] = axl_malloc(16);
|
||||
ASSERT_NOT_NULL(ptrs[i]);
|
||||
*(u32*)ptrs[i] = 0xDEADBEEF;
|
||||
}
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_INT_EQ(0xDEADBEEF, *(u32*)ptrs[i]);
|
||||
axl_free(ptrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Main */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
int main(void)
|
||||
{
|
||||
axl_init();
|
||||
return koan_run_all();
|
||||
}
|
||||
23
makefile
23
makefile
|
|
@ -1,16 +1,29 @@
|
|||
CC = clang
|
||||
CFLAGS = -Wall -Wextra -Werror -pedantic -std=c11 -nostdlib -static -Oz -ffreestanding
|
||||
AXL_SOURCES = $(filter-out main.c %_test.c, $(wildcard *.c))
|
||||
TEST_EXES = $(patsubst %.c,%.exe,$(wildcard *_test.c))
|
||||
TEST_CFLAGS = -Wall -Wextra -Werror -pedantic -std=c11 -static -Oz
|
||||
CFLAGS = $(TEST_CFLAGS) -nostdlib -ffreestanding
|
||||
LDFLAGS = -Wl,/SUBSYSTEM:CONSOLE,/ENTRY:_start -fuse-ld=lld
|
||||
LIBS = -lkernel32
|
||||
SOURCES = $(wildcard *.c)
|
||||
SOURCES = $(AXL_SOURCES) main.c
|
||||
TARGET = prog.exe
|
||||
|
||||
all: clean $(TARGET)
|
||||
all: clean test $(TARGET)
|
||||
|
||||
%_test.exe: %_test.c
|
||||
$(CC) $(TEST_CFLAGS) $(AXL_SOURCES) $< -o $@
|
||||
|
||||
test: $(TEST_EXES)
|
||||
for %%i in ($(TEST_EXES)) do ( \
|
||||
echo Running %%i & \
|
||||
%%i \
|
||||
)
|
||||
|
||||
$(TARGET): $(SOURCES)
|
||||
$(CC) $(CFLAGS) $(SOURCES) -o $(TARGET) $(LDFLAGS) $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET)
|
||||
rm -f $(TEST_EXES)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
.PHONY: all clean test
|
||||
|
|
|
|||
Loading…
Reference in a new issue