From 5d6ca639fdcb56539b629e54dcedbebef82b1ceb Mon Sep 17 00:00:00 2001 From: Patedam Date: Sun, 18 Oct 2026 23:06:41 -0400 Subject: [PATCH] Adding more core layer stuff. Can now boot. --- .agent/rules/coding-guidelines.md | 8 + .clangd | 16 +- .gitmodules | 3 + Juliet/core/core.c | 30 +++ Juliet/core/core.h | 184 ++++++++++++++++++ Juliet/core/core_arena.c | 189 ++++++++++++++++++- Juliet/core/core_arena.h | 67 ++++++- Juliet/core/core_entry_point.c | 1 + Juliet/core/core_inc.c | 9 +- Juliet/core/core_inc.h | 6 + Juliet/core/core_markup.c | 0 Juliet/core/core_markup.h | 3 + Juliet/core/core_memory.c | 1 + Juliet/core/core_memory.h | 8 + Juliet/core/core_processes.c | 0 Juliet/core/core_processes.h | 6 + Juliet/core/core_string.c | 193 ++++++++++++++++++- Juliet/core/core_string.h | 32 ++++ Juliet/core/core_system.c | 1 + Juliet/core/core_system.h | 11 ++ Juliet/core/core_thread_context.c | 71 +++++++ Juliet/core/core_thread_context.h | 29 +++ Juliet/core/core_threads.c | 0 Juliet/core/core_threads.h | 12 ++ Juliet/external/stb | 1 + Juliet/win32/core/win32_core.c | 114 +++++++++++- Juliet/win32/core/win32_core.h | 9 + new_build.bat | 2 +- todo_modules.md | 41 +++++ tools/new_project_gen.c | 297 ++++++++++++++++++++++++++++-- 30 files changed, 1308 insertions(+), 36 deletions(-) create mode 100644 Juliet/core/core_markup.c create mode 100644 Juliet/core/core_markup.h create mode 100644 Juliet/core/core_memory.c create mode 100644 Juliet/core/core_memory.h create mode 100644 Juliet/core/core_processes.c create mode 100644 Juliet/core/core_processes.h create mode 100644 Juliet/core/core_system.c create mode 100644 Juliet/core/core_system.h create mode 100644 Juliet/core/core_thread_context.c create mode 100644 Juliet/core/core_thread_context.h create mode 100644 Juliet/core/core_threads.c create mode 100644 Juliet/core/core_threads.h create mode 160000 Juliet/external/stb create mode 100644 todo_modules.md diff --git a/.agent/rules/coding-guidelines.md b/.agent/rules/coding-guidelines.md index 5b00cff..c5a350d 100644 --- a/.agent/rules/coding-guidelines.md +++ b/.agent/rules/coding-guidelines.md @@ -22,3 +22,11 @@ Struct members: lower_snake_case. Exemple: data, node, next. global/static variables: lower_snake_case prefixed. Exemple: g_my_global. const variables: lower_snake_case prefixed. Exemple: k_my_const_var. Defines and Macros: ALL_CAPS_SNAKE. Exemple: MY_MACRO + +Modular Architecture & Include Rules: +- Juliet uses a single-pass unity build pattern. +- Layer aggregators `_inc.h` and `_inc.c` dictate header and source inclusion order. +- Top-level translation units (e.g. `appbin_main.c`) include the layer `_inc` files in dependency order (`core` -> `render` -> `ui`...). +- Sub-files within a module (e.g. `core.h`, `core_arena.c`, `render.c`) must NEVER directly `#include` other module headers, sibling headers, or configuration defines (`core_defines.h`). +- Never add `#include` directives to sub-files to satisfy standalone LSP/compiler diagnostics. Tooling (`new_project_gen.c` / `compile_commands.json`) must supply preceding includes dynamically based on the base file include order. +- Always explain root causes and confirm with the user before editing project architecture or files. diff --git a/.clangd b/.clangd index e0a1db3..cb3c87d 100644 --- a/.clangd +++ b/.clangd @@ -3,6 +3,9 @@ CompileFlags: - --driver-mode=cl - -Wno-unknown-pragmas - -Wno-unused-command-line-argument + - -Wno-unused-function + - -Wno-undefined-internal + - -Wno-initializer-overrides - /IJuliet Remove: - /Yu* @@ -17,8 +20,15 @@ CompileFlags: Diagnostics: UnusedIncludes: None + Suppress: + - unused-function + - undefined-internal + - initializer-overrides InlayHints: - Enabled: Yes - ParameterNames: Yes - DeducedTypes: Yes + Enabled: true + ParameterNames: true + DeducedTypes: true + Designators: true + BlockEnd: false + TypeNameLimit: 24 diff --git a/.gitmodules b/.gitmodules index 18378da..28e7464 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "External/Imgui"] path = External/Imgui url = https://github.com/ocornut/imgui.git +[submodule "Juliet/external/stb"] + path = Juliet/external/stb + url = https://github.com/nothings/stb.git diff --git a/Juliet/core/core.c b/Juliet/core/core.c index e69de29..e63d834 100644 --- a/Juliet/core/core.c +++ b/Juliet/core/core.c @@ -0,0 +1,30 @@ +// ============================================================================= +// Safe Casts + +internal uint16 safe_cast_uint16(uint32 val) +{ + AssertAlways(val <= max_uint16); + uint16 result = (uint16)val; + return result; +} + +internal uint32 safe_cast_uint32(uint64 val) +{ + AssertAlways(val <= max_uint32); + uint32 result = (uint32)val; + return result; +} + +internal int32 safe_cast_int32(int64 val) +{ + AssertAlways(val <= max_int32); + int32 result = (int32)val; + return result; +} + +// ============================================================================= +// Third party includes + +#define STB_SPRINTF_IMPLEMENTATION +#define STB_SPRINTF_STATIC +#include "external/stb/stb_sprintf.h" diff --git a/Juliet/core/core.h b/Juliet/core/core.h index 96ae9bb..ff36a45 100644 --- a/Juliet/core/core.h +++ b/Juliet/core/core.h @@ -2,9 +2,34 @@ #include +// ============================================================================= +// Third party includes + +#define STB_SPRINTF_DECORATE(name) juliet_##name +#define STB_SPRINTF_STATIC +#include "external/stb/stb_sprintf.h" + #define internal static #define global static +#if (COMPILER_CLANG && OS_WINDOWS) || COMPILER_MSVC +# pragma section(".rdata$", read) +# define read_only __declspec(allocate(".rdata$")) +#elif (COMPILER_CLANG && OS_LINUX) +# define read_only __attribute__((section(".rodata"))) +#else +// Note: not possible with GCC +# define read_only +#endif + +#if COMPILER_CLANG || COMPILER_GCC +# define thread_static __thread +#elif COMPILER_MSVC +# define thread_static __declspec(thread) +#else +# error thread_static not defined for this compiler +#endif + // ============================================================================= // Units @@ -12,17 +37,90 @@ #define MB(d) ((uint64)(d) << 20) #define GB(d) ((uint64)(d) << 30) +// ============================================================================= +// Branch predictor hints + +#if defined(__clang__) +# define Expect(expr, val) __builtin_expect((expr), (val)) +#else +# define Expect(expr, val) (expr) +#endif + +#define Likely(expr) Expect(expr, 1) +#define Unlikely(expr) Expect(expr, 0) + +// ============================================================================= +// Min Max Clamp + +#define Min(A, B) (((A) < (B) ? (A) : (B))) +#define Max(A, B) (((A) > (B) ? (A) : (B))) +#define ClampTop(A, X) Min(A, X) +#define ClampBottom(X, B) Max(X, B) +#define Clamp(A, X, B) (((X) < (A)) ? (A) : ((X) > (B)) ? (B) : (X)) + +// ============================================================================= +// Alignment +#if COMPILER_CLANG || COMPILER_GCC +# define AlignOf(Type) __alignof(Type) +#elif COMPILER_MSVC +# define AlignOf(Type) __alignof(Type) +#else +# error AlignOf not defined for this compiler +#endif + +// ============================================================================= +// Memory Helpers + +#define MemoryCopy(dst, src, size) memmove((dst), (src), (size)) +#define MemorySet(dst, src, size) memset((dst), (src), (size)) + +#define MemoryZero(ptr, size) memset((ptr), 0, (size)) + // ============================================================================= // Asserts +#if COMPILER_CLANG || COMPILER_GCC +# define Trap() __builtin_trap() +#elif COMPILER_MSVC +# define Trap() __debugbreak() +#else +# error Unknown trap intrinsic for this compiler +#endif + +#define AssertAlways(cond) \ + do \ + { \ + if (!(cond)) \ + { \ + Trap(); \ + } \ + } \ + while (0) +#if BUILD_DEBUG +# define Assert(cond) AssertAlways(cond) +#else +# define Assert(cond) (void)(cond) +#endif + #define StaticAssert(cond, ID) global uint8 GLUE(ID, __LINE__)[(cond) ? 1 : -1] +// ============================================================================= +// Linked List helpers + +// Single (link & head) lists (= stacks) +#define SLL_stack_push_node(first, node, next) ((node)->next = (first), (first) = (node)) +#define SLL_stack_pop_node(first, next) ((first) = (first)->next); + // ============================================================================= // Helpers +#define AlignPow2(x, align) (((x) + (align) - 1) & (~((align) - 1))) + #define GLUE_(A, B) A##B #define GLUE(A, B) GLUE_(A, B) +#define ArrayCount(arr) (sizeof(arr) / sizeof((arr)[0])) + // ============================================================================= // Core Types @@ -39,3 +137,89 @@ typedef int8 bool8; typedef int16 bool16; typedef int32 bool32; typedef int64 bool64; + +// ============================================================================= +// Core Constants + +global uint8 max_uint8 = 0xFF; +global uint16 max_uint16 = 0xFFFF; +global uint32 max_uint32 = 0xFFFFFFFF; +global uint64 max_uint64 = 0xFFFFFFFFFFFFFFFFll; + +global int8 max_int8 = (int8)0x7F; +global int16 max_int16 = (int16)0x7FFF; +global int32 max_int32 = (int32)0x7FFFFFFF; +global int64 max_int64 = (int64)0x7FFFFFFFFFFFFFFFll; + +global const uint32 bitmask1 = 0x00000001; +global const uint32 bitmask2 = 0x00000003; +global const uint32 bitmask3 = 0x00000007; +global const uint32 bitmask4 = 0x0000000f; +global const uint32 bitmask5 = 0x0000001f; +global const uint32 bitmask6 = 0x0000003f; +global const uint32 bitmask7 = 0x0000007f; +global const uint32 bitmask8 = 0x000000ff; +global const uint32 bitmask9 = 0x000001ff; +global const uint32 bitmask10 = 0x000003ff; +global const uint32 bitmask11 = 0x000007ff; +global const uint32 bitmask12 = 0x00000fff; +global const uint32 bitmask13 = 0x00001fff; +global const uint32 bitmask14 = 0x00003fff; +global const uint32 bitmask15 = 0x00007fff; +global const uint32 bitmask16 = 0x0000ffff; +global const uint32 bitmask17 = 0x0001ffff; +global const uint32 bitmask18 = 0x0003ffff; +global const uint32 bitmask19 = 0x0007ffff; +global const uint32 bitmask20 = 0x000fffff; +global const uint32 bitmask21 = 0x001fffff; +global const uint32 bitmask22 = 0x003fffff; +global const uint32 bitmask23 = 0x007fffff; +global const uint32 bitmask24 = 0x00ffffff; +global const uint32 bitmask25 = 0x01ffffff; +global const uint32 bitmask26 = 0x03ffffff; +global const uint32 bitmask27 = 0x07ffffff; +global const uint32 bitmask28 = 0x0fffffff; +global const uint32 bitmask29 = 0x1fffffff; +global const uint32 bitmask30 = 0x3fffffff; +global const uint32 bitmask31 = 0x7fffffff; +global const uint32 bitmask32 = 0xffffffff; + +global const uint32 bit1 = (1 << 0); +global const uint32 bit2 = (1 << 1); +global const uint32 bit3 = (1 << 2); +global const uint32 bit4 = (1 << 3); +global const uint32 bit5 = (1 << 4); +global const uint32 bit6 = (1 << 5); +global const uint32 bit7 = (1 << 6); +global const uint32 bit8 = (1 << 7); +global const uint32 bit9 = (1 << 8); +global const uint32 bit10 = (1 << 9); +global const uint32 bit11 = (1 << 10); +global const uint32 bit12 = (1 << 11); +global const uint32 bit13 = (1 << 12); +global const uint32 bit14 = (1 << 13); +global const uint32 bit15 = (1 << 14); +global const uint32 bit16 = (1 << 15); +global const uint32 bit17 = (1 << 16); +global const uint32 bit18 = (1 << 17); +global const uint32 bit19 = (1 << 18); +global const uint32 bit20 = (1 << 19); +global const uint32 bit21 = (1 << 20); +global const uint32 bit22 = (1 << 21); +global const uint32 bit23 = (1 << 22); +global const uint32 bit24 = (1 << 23); +global const uint32 bit25 = (1 << 24); +global const uint32 bit26 = (1 << 25); +global const uint32 bit27 = (1 << 26); +global const uint32 bit28 = (1 << 27); +global const uint32 bit29 = (1 << 28); +global const uint32 bit30 = (1 << 29); +global const uint32 bit31 = (1 << 30); +global const uint32 bit32 = (1 << 31); + +// ============================================================================= +// Safe Casts + +internal uint16 safe_cast_uint16(uint32 val); +internal uint32 safe_cast_uint32(uint64 val); +internal int32 safe_cast_int32(int64 val); diff --git a/Juliet/core/core_arena.c b/Juliet/core/core_arena.c index 4580f44..c6a36db 100644 --- a/Juliet/core/core_arena.c +++ b/Juliet/core/core_arena.c @@ -1,10 +1,193 @@ // ============================================================================= // Arena Functions -internal Arena* arena_alloc_internal() +#include "core/core.h" +internal Arena* arena_alloc_internal(ArenaParams* params) { - Arena* arena = NULL; + uint64 committed = params->commit_size; + uint64 reserved = params->reserve_size; + + // Initial block + void* base = NULL; + if (params->flags & ArenaFlag_LargePages) + { + uint64 page_size = get_system_info()->large_page_size; + reserved = AlignPow2(reserved, page_size); + committed = AlignPow2(committed, page_size); + + base = reserve_memory_large(reserved); + commit_memory_large(base, committed); + } + else + { + uint64 page_size = get_system_info()->page_size; + reserved = AlignPow2(reserved, page_size); + committed = AlignPow2(committed, page_size); + + base = reserve_memory(reserved); + commit_memory(base, committed); + } + + if (Unlikely(base == 0)) + { + abort_self(1); + } + + Arena* arena = base; + arena->current = arena; + arena->commit_size = params->commit_size; + arena->reserve_size = params->reserve_size; + arena->base_position = 0; + arena->position = ARENA_HEADER_SIZE; + arena->committed = committed; + arena->reserved = reserved; + +#if BUILD_DEBUG + arena->alloc_file = params->alloc_file; + arena->alloc_line = params->alloc_line; + arena->name = params->name; +#endif + return arena; } -internal void arena_release(Arena* arena) {} +internal void arena_release(Arena* arena) +{ + for (Arena *p = arena->current, *prev = 0; p != 0; p = prev) + { + prev = p->previous; + release_memory(p, p->reserved); + } +} + +// ============================================================================= +// Arena Push/Pop core Functions + +internal void* arena_push(Arena* arena, uint64 size, uint64 align, bool32 zero) +{ + Arena* current = arena->current; + uint64 pos_pre = AlignPow2(current->position, align); + uint64 pos_post = pos_pre + size; + + uint64 size_to_zero = 0; + if (zero) + { + size_to_zero = Min(current->committed, pos_post) - pos_pre; + } + + if (current->reserved < pos_post && !(arena->flags & ArenaFlag_NoChain)) + { + Arena* new_block = 0; + if (new_block == 0) + { + uint64 reserved_size = arena->reserve_size; + uint64 committed_size = arena->commit_size; + if (size + ARENA_HEADER_SIZE > reserved_size) + { + reserved_size = AlignPow2(size + ARENA_HEADER_SIZE, align); + committed_size = AlignPow2(size + ARENA_HEADER_SIZE, align); + } + new_block = arena_alloc(.reserve_size = reserved_size, .commit_size = committed_size, .flags = arena->flags, + +#if BUILD_DEBUG + .alloc_file = arena->alloc_file, .alloc_line = arena->alloc_line +#endif + ); + size_to_zero = 0; + } + else + { + size_to_zero = size; + } + + new_block->base_position = current->base_position + current->reserved; + SLL_stack_push_node(arena->current, new_block, previous); + + current = new_block; + pos_pre = AlignPow2(current->position, align); + pos_post = pos_pre + size; + } + + // commit new pages if needed + if (current->committed < pos_post) + { + uint64 committed_past_aligned = pos_post + current->commit_size - 1; + committed_past_aligned -= committed_past_aligned % current->commit_size; + uint64 committed_past_clamped = ClampTop(committed_past_aligned, current->reserved); + uint64 committed_size = committed_past_clamped - current->committed; + uint8* committed_ptr = (uint8*)current + current->committed; + if (current->flags & ArenaFlag_LargePages) + { + commit_memory_large(committed_ptr, committed_size); + } + else + { + commit_memory(committed_ptr, committed_size); + } + current->committed = committed_past_clamped; + } + + void* result = 0; + if (current->committed >= pos_post) + { + result = (uint8*)current + pos_pre; + current->position = pos_post; + MemoryZero(result, size_to_zero); + } + + if (Unlikely(result == 0)) + { + abort_self(1); + } + + return result; +} + +internal uint64 arena_pos(Arena* arena) +{ + Arena* current = arena->current; + uint64 position = current->base_position + current->position; + return position; +} + +internal void arena_pop_to(Arena* arena, uint64 position) +{ + uint64 clamped_pos = ClampBottom(ARENA_HEADER_SIZE, position); + Arena* current = arena->current; + + for (Arena* previous = 0; current->base_position >= clamped_pos; current = previous) + { + previous = current->previous; + release_memory(current, current->reserved); + } + arena->current = current; + uint64 new_position = clamped_pos - current->base_position; + AssertAlways(new_position <= current->position); + current->position = new_position; +} + +internal void arena_clear(Arena* arena) +{ + arena_pop_to(arena, 0); +} + +internal void arena_pop(Arena* arena, uint64 amount) +{ + uint64 pos_old = arena_pos(arena); + Assert(pos_old > amount); + uint64 pos_new = pos_old - amount; + arena_pop_to(arena, pos_new); +} + +// Temp arena +internal TempArena temp_begin(Arena* arena) +{ + uint64 position = arena_pos(arena); + TempArena temp = { arena, position }; + return temp; +} + +internal void temp_end(TempArena temp) +{ + arena_pop_to(temp.arena, temp.position); +} diff --git a/Juliet/core/core_arena.h b/Juliet/core/core_arena.h index 8fd2657..964b1d6 100644 --- a/Juliet/core/core_arena.h +++ b/Juliet/core/core_arena.h @@ -5,17 +5,36 @@ // ============================================================================= // Arena Types +typedef uint64 ArenaFlags; +enum +{ + ArenaFlag_NoChain = (1 << 0), + ArenaFlag_LargePages = (1 << 1), +}; + +typedef struct ArenaParams ArenaParams; +struct ArenaParams +{ + ArenaFlags flags; + uint64 commit_size; + uint64 reserve_size; + char* alloc_file; + int alloc_line; + char* name; +}; + typedef struct Arena Arena; struct Arena { - Arena* previous; - Arena* current; - uint64 base_position; - uint64 position; - uint64 commit_size; - uint64 committed; - uint64 reserve_size; - uint64 reserved; + Arena* previous; + Arena* current; + ArenaFlags flags; + uint64 base_position; + uint64 position; + uint64 commit_size; + uint64 committed; + uint64 reserve_size; + uint64 reserved; #if BUILD_DEBUG char* alloc_file; @@ -26,7 +45,7 @@ struct Arena StaticAssert(sizeof(Arena) <= ARENA_HEADER_SIZE, arena_header_size_check); typedef struct TempArena TempArena; -struct TempAren +struct TempArena { Arena* arena; uint64 position; @@ -37,3 +56,33 @@ struct TempAren global uint64 arena_default_reserve_size = MB(64); global uint64 arena_default_commit_size = KB(64); + +internal Arena* arena_alloc_internal(ArenaParams* params); +#define arena_alloc(...) \ + arena_alloc_internal(&(ArenaParams){ .commit_size = arena_default_commit_size, \ + .reserve_size = arena_default_reserve_size, \ + .alloc_file = __FILE__, \ + .alloc_line = __LINE__, \ + __VA_ARGS__ }) +internal void arena_release(Arena* arena); + +// Arena Push/Pop core Functions + +internal void* arena_push(Arena* arena, uint64 size, uint64 align, bool32 zero); +internal uint64 arena_pos(Arena* arena); +internal void arena_pop_to(Arena* arena, uint64 position); +internal void arena_clear(Arena* arena); +internal void arena_pop(Arena* arena, uint64 amount); + +// Temp arena +internal TempArena temp_begin(Arena* arena); +internal void temp_end(TempArena temp); + +// ============================================================================= +// Push Helper Macros + +#define push_array_no_zero_aligned(arr, Type, count, align) \ + (Type*)arena_push((arr), sizeof(Type) * (count), (align), (0)) +#define push_array_aligned(arr, Type, count, align) (Type*)arena_push((arr), sizeof(Type) * (count), (align), (1)) +#define push_array_no_zero(arr, Type, count) push_array_no_zero_aligned(arr, Type, count, Max(8, AlignOf(Type))) +#define push_array(arr, Type, count) push_array_aligned(arr, Type, count, Max(8, AlignOf(Type))) diff --git a/Juliet/core/core_entry_point.c b/Juliet/core/core_entry_point.c index 1c7465a..f5ee83c 100644 --- a/Juliet/core/core_entry_point.c +++ b/Juliet/core/core_entry_point.c @@ -2,5 +2,6 @@ internal void entry_point(); internal void main_thread_core_entry_point(int argc, char** argv) { + ThreadNameF("main_thread"); entry_point(); } diff --git a/Juliet/core/core_inc.c b/Juliet/core/core_inc.c index 0720eaf..85a2527 100644 --- a/Juliet/core/core_inc.c +++ b/Juliet/core/core_inc.c @@ -1,6 +1,13 @@ // clang-format off -#include "core_string.c" +#include "core.c" +#include "core_memory.c" #include "core_arena.c" +#include "core_string.c" +#include "core_system.c" +#include "core_threads.c" +#include "core_thread_context.c" +#include "core_processes.c" +#include "core_markup.c" #include "core_entry_point.c" // clang-format on diff --git a/Juliet/core/core_inc.h b/Juliet/core/core_inc.h index daff020..1b3421b 100644 --- a/Juliet/core/core_inc.h +++ b/Juliet/core/core_inc.h @@ -4,8 +4,14 @@ #include "core_defines.h" #include "core.h" +#include "core_memory.h" +#include "core_processes.h" #include "core_arena.h" #include "core_string.h" +#include "core_system.h" +#include "core_threads.h" +#include "core_thread_context.h" +#include "core_markup.h" #include "core_entry_point.h" // clang-format on diff --git a/Juliet/core/core_markup.c b/Juliet/core/core_markup.c new file mode 100644 index 0000000..e69de29 diff --git a/Juliet/core/core_markup.h b/Juliet/core/core_markup.h new file mode 100644 index 0000000..c47ac1d --- /dev/null +++ b/Juliet/core/core_markup.h @@ -0,0 +1,3 @@ +#pragma once + +#define ThreadNameF(...) (set_thread_namef(__VAR_ARGS__)) diff --git a/Juliet/core/core_memory.c b/Juliet/core/core_memory.c new file mode 100644 index 0000000..7586b68 --- /dev/null +++ b/Juliet/core/core_memory.c @@ -0,0 +1 @@ +// OS DEPEND see os/core/os_core.c diff --git a/Juliet/core/core_memory.h b/Juliet/core/core_memory.h new file mode 100644 index 0000000..690d5ff --- /dev/null +++ b/Juliet/core/core_memory.h @@ -0,0 +1,8 @@ +#pragma once + +internal void* reserve_memory(uint64 size); +internal bool32 commit_memory(void* ptr, uint64 size); +internal void* reserve_memory_large(uint64 size); +internal bool32 commit_memory_large(void* ptr, uint64 size); +internal void decommit_memory(void* ptr, uint64 size); +internal void release_memory(void* ptr, uint64 size); diff --git a/Juliet/core/core_processes.c b/Juliet/core/core_processes.c new file mode 100644 index 0000000..e69de29 diff --git a/Juliet/core/core_processes.h b/Juliet/core/core_processes.h new file mode 100644 index 0000000..b103cc4 --- /dev/null +++ b/Juliet/core/core_processes.h @@ -0,0 +1,6 @@ +#pragma once + +// ============================================================================= +// Aborting (os dependent see os/core/os_core.c) + +internal void abort_self(uint64 exit_code); diff --git a/Juliet/core/core_string.c b/Juliet/core/core_string.c index f90aeae..080d9b2 100644 --- a/Juliet/core/core_string.c +++ b/Juliet/core/core_string.c @@ -1,5 +1,6 @@ // ========================================================================== // // String Length +#include "core/core_arena.h" internal uint64 cstring8_length(uint8* cstring) { uint64 length = 0; @@ -42,7 +43,19 @@ internal uint64 cstring32_length(uint32* cstring) // ========================================================================== // // String Constructor -internal String8 str8_cstring(uint8* cstring) +internal String8 str8(uint8* str, uint64 size) +{ + String8 result = { str, size }; + return result; +} + +internal String8 str8_zero() +{ + String8 result = { 0 }; + return result; +} + +internal String8 str8_cstring(const char* cstring) { String8 result = { (uint8*)cstring, cstring8_length((uint8*)cstring) }; return result; @@ -59,3 +72,181 @@ internal String32 str32_cstring(uint32* cstring) String32 result = { (uint32*)cstring, cstring32_length((uint32*)cstring) }; return result; } + +// ========================================================================== // +// String Formatting & Copying + +internal String8 str8_copy(Arena* arena, String8 in) +{ + String8 str; + str.size = in.size; + str.str = push_array_no_zero(arena, uint8, str.size + 1); + MemoryCopy(str.str, in.str, in.size); + str.str[str.size] = '\0'; + return str; +} + +// ============================================================================= +// Unicode String Encode/Decode + +read_only global uint8 utf8_class[32] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 5, +}; + +internal UnicodeDecode utf8_decode(uint8* str, uint64 max) +{ + UnicodeDecode result = { 1, max_uint32 }; + uint8 byte = str[0]; + uint8 byte_class = utf8_class[byte >> 3]; + switch (byte_class) + { + case 1: + { + result.codepoint = byte; + break; + } + case 2: + { + if (1 < max) + { + uint8 cont_byte = str[1]; + if (utf8_class[cont_byte >> 3] == 0) + { + result.codepoint = (byte & bitmask5) << 6; + result.codepoint |= (cont_byte & bitmask6); + result.increment = 2; + } + } + break; + } + case 3: + { + if (2 < max) + { + uint8 cont_byte[2] = { str[1], str[2] }; + if (utf8_class[cont_byte[0] >> 3] == 0 && utf8_class[cont_byte[1] >> 3] == 0) + { + result.codepoint = (byte & bitmask4) << 12; + result.codepoint |= ((cont_byte[0] & bitmask6) << 6); + result.codepoint |= (cont_byte[1] & bitmask6); + result.increment = 3; + } + } + break; + } + case 4: + { + if (3 < max) + { + uint8 cont_byte[3] = { str[1], str[2], str[3] }; + if (utf8_class[cont_byte[0] >> 3] == 0 && utf8_class[cont_byte[1] >> 3] == 0 && utf8_class[cont_byte[2] >> 3] == 0) + { + result.codepoint = (byte & bitmask3) << 18; + result.codepoint |= ((cont_byte[0] & bitmask6 << 12)); + result.codepoint |= ((cont_byte[1] & bitmask6) << 6); + result.codepoint |= (cont_byte[2] & bitmask6); + result.increment = 4; + } + } + break; + } + } + return result; +} + +internal UnicodeDecode utf16_decode(uint16* str, uint64 max) +{ + UnicodeDecode result = { 1, max_uint32 }; + result.codepoint = str[0]; + result.increment = 1; + if (max > 1 && 0xD800 <= str[0] && str[0] < 0xDC00 && 0xDC00 <= str[1] && str[1] < 0xE000) + { + result.codepoint = ((str[0] - 0xD800) << 10) | ((str[1] - 0xDC00) + 0x10000); + result.increment = 2; + } + return result; +} + +internal uint32 utf8_encode(uint8* str, uint32 codepoint) +{ + uint32 increment = 0; + if (codepoint <= 0x7f) + { + str[0] = (uint8)codepoint; + increment = 1; + } + else if (codepoint <= 0x7FF) + { + str[0] = (bitmask2 << 6) | ((codepoint >> 6) & bitmask5); + str[1] = bit8 | (codepoint & bitmask6); + increment = 2; + } + else if (codepoint <= 0xFFFF) + { + str[0] = (bitmask3 << 5) | ((codepoint >> 12) & bitmask4); + str[1] = bit8 | ((codepoint >> 6) & bitmask6); + str[2] = bit8 | (codepoint & bitmask6); + increment = 3; + } + else if (codepoint <= 0x10FFFF) + { + str[0] = (bitmask4 << 4) | ((codepoint >> 18) & bitmask3); + str[1] = bit8 | ((codepoint >> 12) & bitmask6); + str[2] = bit8 | (codepoint >> 6 & bitmask6); + str[3] = bit8 | (codepoint & bitmask6); + increment = 4; + } + else + { + str[0] = '?'; + increment = 1; + } + return increment; +} + +internal uint32 utf16_encode(uint16* str, uint32 codepoint) +{ + uint32 increment = 1; + if (codepoint == max_uint32) + { + str[0] = (uint16)'?'; + } + else if (codepoint < 0x10000) + { + str[0] = (uint16)codepoint; + } + else + { + uint32 v = codepoint - 0x10000; + str[0] = safe_cast_uint16(0xD800 + (v >> 10)); + str[1] = safe_cast_uint16(0xDC00 + (v & bitmask10)); + increment = 2; + } + return increment; +} + +// ============================================================================= +// Unicode String Conversions + +internal String8 str8_from_16(Arena* arena, String16 in) +{ + String8 result = str8_zero(); + if (in.size) + { + uint64 capacity = in.size * 3; + uint8* str = push_array_no_zero(arena, uint8, capacity + 1); + uint16* ptr = in.str; + uint16* op1 = ptr + in.size; + uint64 size = 0; + UnicodeDecode consume; + for (; ptr < op1; ptr += consume.increment) + { + consume = utf16_decode(ptr, op1 - ptr); + size += utf8_encode(str + size, consume.codepoint); + } + str[size] = '\0'; + arena_pop(arena, (capacity - size)); + result = str8(str, size); + } + return result; +} diff --git a/Juliet/core/core_string.h b/Juliet/core/core_string.h index 7ba7f17..d2b1ec6 100644 --- a/Juliet/core/core_string.h +++ b/Juliet/core/core_string.h @@ -24,6 +24,16 @@ struct String32 uint64 size; }; +// ========================================================================== // +// Unicode decoding Types + +typedef struct UnicodeDecode UnicodeDecode; +struct UnicodeDecode +{ + uint32 increment; + uint32 codepoint; +}; + // ========================================================================== // // String Length internal uint64 cstring8_length(uint8* cstring); @@ -33,4 +43,26 @@ internal uint64 cstring32_length(uint32* cstring); // ========================================================================== // // String Constructor +internal String8 str8(uint8* str, uint64 size); +internal String8 str8_zero(); +internal String8 str8_cstring(const char* cstring); internal String16 str16_cstring(uint16* cstring); +internal String32 str32_cstring(uint32* cstring); + +// ========================================================================== // +// String Formatting & Copying + +internal String8 str8_copy(Arena* arena, String8 in); + +// ========================================================================== // +// Unicode String Encode/Decode + +internal UnicodeDecode utf8_decode(uint8* str, uint64 max); +internal UnicodeDecode utf16_decode(uint16* str, uint64 max); +internal uint32 utf8_encode(uint8* str, uint32 codepoint); +internal uint32 utf16_encode(uint16* str, uint32 codepoint); + +// ========================================================================== // +// Unicode String Conversions + +internal String8 str8_from_16(Arena* arena, String16 in); diff --git a/Juliet/core/core_system.c b/Juliet/core/core_system.c new file mode 100644 index 0000000..7586b68 --- /dev/null +++ b/Juliet/core/core_system.c @@ -0,0 +1 @@ +// OS DEPEND see os/core/os_core.c diff --git a/Juliet/core/core_system.h b/Juliet/core/core_system.h new file mode 100644 index 0000000..8a07dc6 --- /dev/null +++ b/Juliet/core/core_system.h @@ -0,0 +1,11 @@ +#pragma once + +typedef struct SystemInfo SystemInfo; +struct SystemInfo +{ + uint64 page_size; + uint64 large_page_size; + String8 machine_name; +}; + +internal SystemInfo* get_system_info(); diff --git a/Juliet/core/core_thread_context.c b/Juliet/core/core_thread_context.c new file mode 100644 index 0000000..b9910c3 --- /dev/null +++ b/Juliet/core/core_thread_context.c @@ -0,0 +1,71 @@ +// ============================================================================= +// Globals + +thread_static TCTX* tctx_thread_local; + +// ============================================================================= +// Thread Context Functions + +// Thread Context allocation +internal TCTX* tctx_alloc() +{ +#if BUILD_DEBUG + thread_static static char name[2][1024]; + juliet_snprintf(name[0], sizeof(name[0]), "Scratch/0[TID:%u]", tid()); + juliet_snprintf(name[1], sizeof(name[1]), "Scratch/1[TID:%u]", tid()); + Arena* arena_0 = arena_alloc(.name = name[0]); + Arena* arena_1 = arena_alloc(.name = name[1]); +#else + Arena* arena_0 = arena_alloc(); + Arena* arena_1 = arena_alloc(); +#endif + TCTX* tctx = push_array(arena_0, TCTX, 1); + tctx->arenas[0] = arena_0; + tctx->arenas[1] = arena_1; + return tctx; +} + +internal void tctx_release(TCTX* tctx) +{ + arena_release(tctx->arenas[0]); + arena_release(tctx->arenas[1]); +} + +internal void tctx_select(TCTX* tctx) +{ + tctx_thread_local = tctx; +} + +internal TCTX* tctx_selected() +{ + return tctx_thread_local; +} + +internal Arena* tctx_get_scratch(Arena** conflicts, uint64 count) +{ + TCTX* tctx = tctx_selected(); + Arena* result = 0; + Arena** arena_ptr = tctx->arenas; + for (uint64 i = 0; i < ArrayCount(tctx->arenas); ++i, ++arena_ptr) + { + Arena** conflict_ptr = conflicts; + bool32 has_conflict = 0; + for (uint64 j = 0; j < count; ++j, ++conflict_ptr) + { + if (*arena_ptr == *conflict_ptr) + { + has_conflict = 1; + break; + } + } + if (!has_conflict) + { + result = *arena_ptr; + break; + } + } + return result; +} + +#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count))) +#define scratch_end(scratch) temp_end(scratch) diff --git a/Juliet/core/core_thread_context.h b/Juliet/core/core_thread_context.h new file mode 100644 index 0000000..6d082b2 --- /dev/null +++ b/Juliet/core/core_thread_context.h @@ -0,0 +1,29 @@ +#pragma once + +typedef struct TCTX TCTX; +struct TCTX +{ + // Scratch arenas + Arena* arenas[2]; + + uint8 thread_name[32]; + uint64 thread_name_size; + + char* file_name; + uint64 line_number; +}; + +extern thread_static TCTX* tctx_thread_local; + +// ============================================================================= +// Thread Context Functions + +// Thread Context allocation +internal TCTX* tctx_alloc(); +internal void tctx_release(TCTX* tctx); +internal void tctx_select(TCTX* tctx); +internal TCTX* tctx_selected(); + +internal Arena* tctx_get_scratch(Arena** conflicts, uint64 count); +#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count))) +#define scratch_end(scratch) temp_end(scratch) diff --git a/Juliet/core/core_threads.c b/Juliet/core/core_threads.c new file mode 100644 index 0000000..e69de29 diff --git a/Juliet/core/core_threads.h b/Juliet/core/core_threads.h new file mode 100644 index 0000000..61ff3f2 --- /dev/null +++ b/Juliet/core/core_threads.h @@ -0,0 +1,12 @@ +#pragma once + +// ============================================================================= +// Thread Info Helpers +internal void set_thread_name(String8 name); +internal void set_thread_namef(char* fmt, ...); + +// ============================================================================= +// Thread Info + +internal uint32 tid(); +internal void set_platform_thread_name(String8 name); diff --git a/Juliet/external/stb b/Juliet/external/stb new file mode 160000 index 0000000..2c980bb --- /dev/null +++ b/Juliet/external/stb @@ -0,0 +1 @@ +Subproject commit 2c980bb59875b0d32144a71867fbdebb2f77cd20 diff --git a/Juliet/win32/core/win32_core.c b/Juliet/win32/core/win32_core.c index f8633c2..3e48ee6 100644 --- a/Juliet/win32/core/win32_core.c +++ b/Juliet/win32/core/win32_core.c @@ -1,17 +1,127 @@ +// ============================================================================= +// System + +#include "core/core_thread_context.h" +internal SystemInfo* get_system_info() +{ + return &w32_state.system_info; +} + +// ============================================================================= +// Thread Info + +internal uint32 tid() +{ + DWORD id = GetCurrentThreadId(); + return (uint32)id; +} + +internal void set_platform_thread_name(String8 name) +{ + TempArena scratch = scratch_begin(0, 0); + + scratch_end(scratch); +} + +// ============================================================================= +// Memory + +internal void* reserve_memory(uint64 size) +{ + void* result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); + return result; +} + +internal bool32 commit_memory(void* ptr, uint64 size) +{ + bool32 result = (VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) != 0); + return result; +} + +internal void* reserve_memory_large(uint64 size) +{ + // Windows do not allow commiting large pages + void* result = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); + return result; +} + +internal bool32 commit_memory_large(void* ptr, uint64 size) +{ + (void)ptr; + (void)size; + return 1; +} + +internal void decommit_memory(void* ptr, uint64 size) +{ + VirtualFree(ptr, size, MEM_DECOMMIT); +} + +internal void release_memory(void* ptr, uint64 size) +{ + (void)size; // Unused on windows + VirtualFree(ptr, 0, MEM_RELEASE); +} + +// ============================================================================= +// Aborting + +internal void abort_self(uint64 exit_code) +{ + ExitProcess((int)exit_code); +} + +// ============================================================================= +// Entry points + internal void w32_entry_point(int argc, WCHAR** wargv) { - char* argv[10]; + SYSTEM_INFO sys_info = { 0 }; + GetSystemInfo(&sys_info); + + SystemInfo* info = &w32_state.system_info; + info->page_size = sys_info.dwPageSize; + info->large_page_size = GetLargePageMinimum(); + + Arena* args_arena = arena_alloc(.reserve_size = MB(1), .commit_size = KB(32)); + char** argv = push_array(args_arena, char*, argc); for (int i = 0; i < argc; ++i) { String16 arg16 = str16_cstring((uint16*)wargv[i]); String8 arg8 = str8_from_16(args_arena, arg16); argv[i] = (char*)arg8.str; } - main_thread_core_entry_point(argc, &argv); + + // Thread context + TCTX* tctx = tctx_alloc(); + tctx_select(tctx); + + // setup w32 state + Arena* arena = arena_alloc(); + { + w32_state.arena = arena; + { + SystemInfo* info = &w32_state.system_info; + DWORD size = MAX_COMPUTERNAME_LENGTH + 1; + uint8 buffer[MAX_COMPUTERNAME_LENGTH + 1] = { 0 }; + if (GetComputerNameA((char*)buffer, &size)) + { + info->machine_name = str8_copy(arena, str8(buffer, size)); + } + } + } + + main_thread_core_entry_point(argc, argv); + + tctx_release(tctx); } int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) { + (void)hInstance; + (void)hPrevInstance; + (void)lpCmdLine; + (void)nShowCmd; w32_entry_point(__argc, __wargv); return 0; } diff --git a/Juliet/win32/core/win32_core.h b/Juliet/win32/core/win32_core.h index 9475762..cc1a3d5 100644 --- a/Juliet/win32/core/win32_core.h +++ b/Juliet/win32/core/win32_core.h @@ -2,3 +2,12 @@ #include #pragma comment(lib, "user32") + +typedef struct W32_State W32_State; +struct W32_State +{ + Arena* arena; + SystemInfo system_info; +}; + +global W32_State w32_state = { 0 }; diff --git a/new_build.bat b/new_build.bat index 12e8799..d8ebbe2 100644 --- a/new_build.bat +++ b/new_build.bat @@ -4,7 +4,7 @@ cd /D "%~dp0" :: --- Usage Notes ------------------------------------------------------------- :: -:: Central build script for Juliet new module architecture (inspired by raddbg). +:: Central build script for Juliet modular architecture. :: Takes alphanumeric arguments to control target, compiler, and build configuration. :: :: Examples: diff --git a/todo_modules.md b/todo_modules.md new file mode 100644 index 0000000..86a11e9 --- /dev/null +++ b/todo_modules.md @@ -0,0 +1,41 @@ +# Modular Architecture & Tooling TODO + +This document tracks upcoming tasks for Juliet's modular architecture (`core`, `appbin`, future `render`, `ui`, etc.) and the build/LSP generation tooling. + +--- + +## 1. Tooling & Project Generator (`tools/new_project_gen.c`) +- [x] **Dynamic Preceding Include Parser**: + - Implemented dynamic parser `get_preceding_includes_from_inc_h(layer_name, target_file_path)` in `tools/new_project_gen.c`. + - Reads `_inc.h` line-by-line and collects all `#include "..."` statements that appear strictly **above** the target header being analyzed. + - Automatically injects those preceding includes via `/FI` so that any header opened in Neovim/clangd has its exact required preprocessor context with zero hardcoding. +- [x] **Multi-Layer Cascade**: + - Deduces layer hierarchy from `appbin_main.c` and supplies lower layer includes (`/FIcore/core_inc.h`, etc.) to upper layers automatically. + +--- + +## 2. Core Module Implementation (`Juliet/core/`) +- [ ] **Fill `core.c`**: + - Implement base core routines (OS helpers, string utilities, arena allocation implementations, math helpers). +- [ ] **Win32 Core Cleanup**: + - Fix argument passing in `win32_core.c:55` (`main_thread_core_entry_point(argc, &argv)` vs `char** argv`). + - Implement proper command line argument parsing into `String8`/`String16` arenas. +- [ ] **Unit Tests for Core**: + - Create standalone test executable or test entry point validating arena allocation, commit/decommit, and string routines. + +--- + +## 3. Future Layers +- [ ] **Render Layer (`Juliet/render/`)**: + - Create `render_inc.h`, `render_inc.c`. + - Implement D3D12 device initialization, command queues, swap chain, and memory allocators. + - Add `#include "render/render_inc.h"` and `#include "render/render_inc.c"` to `appbin_main.c`. +- [ ] **UI Layer (`Juliet/ui/`)**: + - Create `ui_inc.h`, `ui_inc.c`. + - Implement UI hierarchy, layout, text rendering, and input handling. + +--- + +## 4. Build System & LSP Maintenance +- [ ] Run `new_generate.bat` after adding new layers or changing layer include order in `appbin_main.c`. +- [ ] Keep `new_build.bat` as the single-pass unity compiler command for lightning-fast build times. diff --git a/tools/new_project_gen.c b/tools/new_project_gen.c index b534cb0..48df853 100644 --- a/tools/new_project_gen.c +++ b/tools/new_project_gen.c @@ -248,6 +248,241 @@ static void get_normalized_cwd(char* out_cwd, size_t out_cwd_size, char* out_rea } } +static int32 get_layer_order_from_main(char layers[32][64]) +{ + int32 count = 0; + FILE* file = fopen("Juliet/appbin/appbin_main.c", "rb"); + if (file != NULL) + { + char line[512]; + while (fgets(line, sizeof(line), file) != NULL) + { + char* inc_pos = strstr(line, "#include"); + if (inc_pos != NULL) + { + char* quote_start = strchr(inc_pos, '"'); + if (quote_start != NULL) + { + quote_start++; + char* slash = strchr(quote_start, '/'); + if (slash == NULL) + { + slash = strchr(quote_start, '\\'); + } + if (slash != NULL) + { + size_t layer_len = (size_t)(slash - quote_start); + if (layer_len > 0 && layer_len < 64) + { + char layer_name[64]; + memcpy(layer_name, quote_start, layer_len); + layer_name[layer_len] = '\0'; + + char* quote_end = strchr(slash, '"'); + if (quote_end != NULL && strstr(slash, "_inc.h") != NULL) + { + bool already_added = false; + for (int32 i = 0; i < count; ++i) + { + if (_stricmp(layers[i], layer_name) == 0) + { + already_added = true; + break; + } + } + if (!already_added && count < 32) + { + strncpy_s(layers[count], sizeof(layers[count]), layer_name, _TRUNCATE); + count++; + } + } + } + } + } + } + } + fclose(file); + } + + // Always ensure "core" is present + bool has_core = false; + for (int32 i = 0; i < count; ++i) + { + if (_stricmp(layers[i], "core") == 0) + { + has_core = true; + break; + } + } + if (!has_core && count < 32) + { + for (int32 i = count; i > 0; --i) + { + strncpy_s(layers[i], sizeof(layers[i]), layers[i - 1], _TRUNCATE); + } + strncpy_s(layers[0], sizeof(layers[0]), "core", _TRUNCATE); + count++; + } + + // Discover any other directories in Juliet/ that have _inc.h but weren't in appbin_main.c yet + WIN32_FIND_DATAA find_data; + HANDLE find_handle = FindFirstFileA("Juliet\\*", &find_data); + if (find_handle != INVALID_HANDLE_VALUE) + { + do + { + if (strcmp(find_data.cFileName, ".") == 0 || strcmp(find_data.cFileName, "..") == 0) + { + continue; + } + if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (_stricmp(find_data.cFileName, "src") == 0 || + _stricmp(find_data.cFileName, "include") == 0 || + _stricmp(find_data.cFileName, "appbin") == 0) + { + continue; + } + bool already = false; + for (int32 i = 0; i < count; ++i) + { + if (_stricmp(layers[i], find_data.cFileName) == 0) + { + already = true; + break; + } + } + if (!already && count < 32) + { + strncpy_s(layers[count], sizeof(layers[count]), find_data.cFileName, _TRUNCATE); + count++; + } + } + } while (FindNextFileA(find_handle, &find_data)); + FindClose(find_handle); + } + + return count; +} + +static int32 find_layer_index_for_file(const char* file_path, char layers[32][64], int32 layer_count) +{ + Assert(file_path != NULL); + + if (strstr(file_path, "win32/core") != NULL || strstr(file_path, "win32\\core") != NULL) + { + for (int32 i = 0; i < layer_count; ++i) + { + if (_stricmp(layers[i], "core") == 0) + { + return i; + } + } + } + + for (int32 i = 0; i < layer_count; ++i) + { + char prefix1[128]; + char prefix2[128]; + snprintf(prefix1, sizeof(prefix1), "Juliet/%s/", layers[i]); + snprintf(prefix2, sizeof(prefix2), "Juliet\\%s\\", layers[i]); + if (_strnicmp(file_path, prefix1, strlen(prefix1)) == 0 || + _strnicmp(file_path, prefix2, strlen(prefix2)) == 0) + { + return i; + } + } + + return -1; +} + +static void get_preceding_includes_from_inc_h(const char* layer_name, const char* target_file_path, char* out_flags, size_t out_flags_size) +{ + Assert(layer_name != NULL); + Assert(target_file_path != NULL); + Assert(out_flags != NULL); + + char inc_h_path[512]; + snprintf(inc_h_path, sizeof(inc_h_path), "Juliet/%s/%s_inc.h", layer_name, layer_name); + + FILE* file = fopen(inc_h_path, "rb"); + if (file == NULL) + { + return; + } + + char line[512]; + while (fgets(line, sizeof(line), file) != NULL) + { + char* hash = strchr(line, '#'); + if (hash == NULL) + { + continue; + } + char* inc_pos = strstr(hash, "include"); + if (inc_pos == NULL) + { + continue; + } + + char* quote_start = strchr(inc_pos, '"'); + if (quote_start == NULL) + { + continue; + } + quote_start++; + char* quote_end = strchr(quote_start, '"'); + if (quote_end == NULL) + { + continue; + } + + size_t inc_file_len = (size_t)(quote_end - quote_start); + if (inc_file_len == 0 || inc_file_len >= 256) + { + continue; + } + + char inc_file[256]; + memcpy(inc_file, quote_start, inc_file_len); + inc_file[inc_file_len] = '\0'; + normalize_path_slashes_forward(inc_file); + + char full_inc_path[512]; + if (strstr(inc_file, "/") != NULL) + { + snprintf(full_inc_path, sizeof(full_inc_path), "Juliet/%s", inc_file); + } + else + { + snprintf(full_inc_path, sizeof(full_inc_path), "Juliet/%s/%s", layer_name, inc_file); + } + normalize_path_slashes_forward(full_inc_path); + + char expected_suffix[256]; + snprintf(expected_suffix, sizeof(expected_suffix), "/%s", inc_file); + + if (_stricmp(full_inc_path, target_file_path) == 0 || + str_ends_with_no_case(target_file_path, expected_suffix)) + { + break; + } + + char flag[512]; + if (strstr(inc_file, "/") != NULL) + { + snprintf(flag, sizeof(flag), " \"/FI%s\",\n", inc_file); + } + else + { + snprintf(flag, sizeof(flag), " \"/FI%s/%s\",\n", layer_name, inc_file); + } + strncat_s(out_flags, out_flags_size, flag, _TRUNCATE); + } + + fclose(file); +} + static void generate_compile_commands(void) { char cwd[512]; @@ -263,8 +498,10 @@ static void generate_compile_commands(void) bool is_first = true; - // 1. Scan new Juliet modules (core, appbin, etc.) - // We scan folders directly inside Juliet/, ignoring legacy 'src' and 'include' + char layers[32][64]; + int32 layer_count = get_layer_order_from_main(layers); + + // 1. Scan new Juliet modules (core, render, ui, appbin, etc.) WIN32_FIND_DATAA find_data; HANDLE find_handle = FindFirstFileA("Juliet\\*", &find_data); if (find_handle != INVALID_HANDLE_VALUE) @@ -287,21 +524,49 @@ static void generate_compile_commands(void) snprintf(module_dir, sizeof(module_dir), "Juliet\\%s", find_data.cFileName); FileList module_files = {0}; - find_files_recursive(module_dir, &module_files, false); - - char module_name[64]; - strncpy_s(module_name, sizeof(module_name), find_data.cFileName, _TRUNCATE); - - char module_inc_flag[128] = ""; - char module_inc_disk_path[512]; - snprintf(module_inc_disk_path, sizeof(module_inc_disk_path), "Juliet\\%s\\%s_inc.h", module_name, module_name); - if (_stricmp(module_name, "core") != 0 && GetFileAttributesA(module_inc_disk_path) != INVALID_FILE_ATTRIBUTES) - { - snprintf(module_inc_flag, sizeof(module_inc_flag), " \"/FI%s/%s_inc.h\",\n", module_name, module_name); - } + find_files_recursive(module_dir, &module_files, true); for (FileNode* node = module_files.first; node != NULL; node = node->next) { + char forced_inc_flags[1024] = ""; + int32 file_layer_idx = find_layer_index_for_file(node->path, layers, layer_count); + + if (file_layer_idx >= 0 && _stricmp(layers[file_layer_idx], "appbin") != 0) + { + for (int32 i = 0; i <= file_layer_idx; ++i) + { + if (i < file_layer_idx) + { + char inc_disk_path[512]; + snprintf(inc_disk_path, sizeof(inc_disk_path), "Juliet\\%s\\%s_inc.h", layers[i], layers[i]); + if (GetFileAttributesA(inc_disk_path) != INVALID_FILE_ATTRIBUTES) + { + char flag[128]; + snprintf(flag, sizeof(flag), " \"/FI%s/%s_inc.h\",\n", layers[i], layers[i]); + strncat_s(forced_inc_flags, sizeof(forced_inc_flags), flag, _TRUNCATE); + } + } + else + { + if (is_c_source_file(node->path)) + { + char inc_disk_path[512]; + snprintf(inc_disk_path, sizeof(inc_disk_path), "Juliet\\%s\\%s_inc.h", layers[i], layers[i]); + if (GetFileAttributesA(inc_disk_path) != INVALID_FILE_ATTRIBUTES) + { + char flag[128]; + snprintf(flag, sizeof(flag), " \"/FI%s/%s_inc.h\",\n", layers[i], layers[i]); + strncat_s(forced_inc_flags, sizeof(forced_inc_flags), flag, _TRUNCATE); + } + } + else if (!str_ends_with_no_case(node->path, "_inc.h")) + { + get_preceding_includes_from_inc_h(layers[i], node->path, forced_inc_flags, sizeof(forced_inc_flags)); + } + } + } + } + const char* dirs_to_emit[2] = { cwd, (real_cwd[0] && _stricmp(cwd, real_cwd) != 0) ? real_cwd : NULL }; for (int32 d = 0; d < 2; d++) @@ -331,16 +596,16 @@ static void generate_compile_commands(void) " \"/clang:-std=c11\",\n" " \"/W4\",\n" " \"/Wno-unused-function\",\n" + " \"/clang:-Wno-initializer-overrides\",\n" " \"/utf-8\",\n" " \"/DBUILD_DEBUG=1\",\n" " \"/IJuliet\",\n" - " \"/FIcore/core_inc.h\",\n" "%s" " \"-c\",\n" " \"%s\"\n" " ]\n" " }", - dirs_to_emit[d], full_file_path, module_inc_flag, full_file_path); + dirs_to_emit[d], full_file_path, forced_inc_flags, full_file_path); } }