+++ /dev/null
-# # ---
-# # Language: Cpp
-# # # BasedOnStyle: Mozilla
-# # AccessModifierOffset: -2
-# # AlignAfterOpenBracket: Align
-# AlignConsecutiveMacros: false
-# AlignConsecutiveAssignments: false
-# AlignConsecutiveDeclarations: false
-# AlignEscapedNewlines: Right
-# AlignOperands: true
-# AlignTrailingComments: true
-# # AllowAllArgumentsOnNextLine: true
-# # AllowAllConstructorInitializersOnNextLine: true
-# # AllowAllParametersOfDeclarationOnNextLine: false
-# AllowShortBlocksOnASingleLine: Never
-# # AllowShortCaseLabelsOnASingleLine: false
-# AllowShortFunctionsOnASingleLine: Inline
-# # AllowShortLambdasOnASingleLine: All
-# # AllowShortIfStatementsOnASingleLine: Never
-# # AllowShortLoopsOnASingleLine: false
-# # AlwaysBreakAfterDefinitionReturnType: TopLevel
-# AlwaysBreakAfterReturnType: TopLevel
-# # AlwaysBreakBeforeMultilineStrings: false
-# AlwaysBreakTemplateDeclarations: Yes
-# BinPackArguments: false
-# BinPackParameters: false
-# BraceWrapping:
-# AfterCaseLabel: false
-# AfterClass: true
-# AfterControlStatement: false
-# AfterEnum: true
-# AfterFunction: true
-# AfterNamespace: false
-# AfterObjCDeclaration: false
-# AfterStruct: true
-# AfterUnion: true
-# AfterExternBlock: true
-# BeforeCatch: false
-# BeforeElse: false
-# IndentBraces: false
-# SplitEmptyFunction: true
-# SplitEmptyRecord: false
-# SplitEmptyNamespace: true
-# # BreakBeforeBinaryOperators: None
-# BreakBeforeBraces: Mozilla
-# # BreakBeforeInheritanceComma: false
-# # BreakInheritanceList: BeforeComma
-# BreakBeforeTernaryOperators: true
-# # BreakConstructorInitializersBeforeComma: false
-# # BreakConstructorInitializers: BeforeComma
-# # BreakAfterJavaFieldAnnotations: false
-# BreakStringLiterals: true
-# ColumnLimit: 80
-# # CommentPragmas: "^ IWYU pragma:"
-# # CompactNamespaces: false
-# # ConstructorInitializerAllOnOneLineOrOnePerLine: false
-# # ConstructorInitializerIndentWidth: 2
-# ContinuationIndentWidth: 2
-# # Cpp11BracedListStyle: false
-# # DeriveLineEnding: true
-# # DerivePointerAlignment: false
-# # DisableFormat: false
-# # ExperimentalAutoDetectBinPacking: false
-# # FixNamespaceComments: false
-# ForEachMacros:
-# - foreach
-# - Q_FOREACH
-# - BOOST_FOREACH
-# # IncludeBlocks: Preserve
-# # IncludeCategories:
-# # - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
-# # Priority: 2
-# # SortPriority: 0
-# # - Regex: '^(<|"(gtest|gmock|isl|json)/)'
-# # Priority: 3
-# # SortPriority: 0
-# # - Regex: ".*"
-# # Priority: 1
-# # SortPriority: 0
-# # IncludeIsMainRegex: "(Test)?$"
-# # IncludeIsMainSourceRegex: ""
-# IndentCaseLabels: true
-# IndentGotoLabels: true
-# IndentPPDirectives: None
-# IndentWidth: 4
-# # IndentWrappedFunctionNames: false
-# # JavaScriptQuotes: Leave
-# # JavaScriptWrapImports: true
-# KeepEmptyLinesAtTheStartOfBlocks: true
-# # MacroBlockBegin: ""
-# # MacroBlockEnd: ""
-# MaxEmptyLinesToKeep: 1
-# # NamespaceIndentation: None
-# # ObjCBinPackProtocolList: Auto
-# # ObjCBlockIndentWidth: 2
-# # ObjCSpaceAfterProperty: true
-# # ObjCSpaceBeforeProtocolList: false
-# # PenaltyBreakAssignment: 2
-# # PenaltyBreakBeforeFirstCallParameter: 19
-# # PenaltyBreakComment: 300
-# # PenaltyBreakFirstLessLess: 120
-# # PenaltyBreakString: 1000
-# # PenaltyBreakTemplateDeclaration: 10
-# # PenaltyExcessCharacter: 1000000
-# # PenaltyReturnTypeOnItsOwnLine: 200
-# PointerAlignment: Left
-# # ReflowComments: true
-# SortIncludes: true
-# # SortUsingDeclarations: true
-# SpaceAfterCStyleCast: false
-# SpaceAfterLogicalNot: false
-# SpaceAfterTemplateKeyword: false
-# SpaceBeforeAssignmentOperators: true
-# SpaceBeforeCpp11BracedList: false
-# SpaceBeforeCtorInitializerColon: true
-# SpaceBeforeInheritanceColon: true
-# SpaceBeforeParens: ControlStatements
-# SpaceBeforeRangeBasedForLoopColon: true
-# SpaceInEmptyBlock: true
-# SpaceInEmptyParentheses: false
-# SpacesBeforeTrailingComments: 1
-# SpacesInAngles: false
-# SpacesInConditionalStatement: false
-# SpacesInContainerLiterals: true
-# SpacesInCStyleCastParentheses: false
-# SpacesInParentheses: false
-# SpacesInSquareBrackets: false
-# SpaceBeforeSquareBrackets: false
-# Standard: Latest
-# StatementMacros:
-# - Q_UNUSED
-# - QT_REQUIRE_VERSION
-# TypenameMacros:
-# - optimize
-# - noret
-# - weak
-# - weak_alias
-# # TabWidth: 8
-# UseCRLF: false
-# UseTab: Never
-# # ---
-
unused/
__pycache__/
+.builder/
+.config.json
+
.gdb_history
**.o
+++ /dev/null
-*.S.inc
\ No newline at end of file
"-ffreestanding",
"-D__ARCH__=i386",
"-D__LUNAIXOS_DEBUG__",
- "-include flags.h",
- "-include config.h"
+ "-include .builder/configs.h"
],
"defines": [],
"compilerPath": "${HOME}/opt/i686-gcc-12/bin/i686-elf-gcc",
--- /dev/null
+use("kernel")
+use("libs")
+use("arch")
+use("hal")
+
+headers([
+ "includes",
+ "includes/usr"
+])
\ No newline at end of file
--- /dev/null
+import time
+
+include("kernel/LConfig")
+include("arch/LConfig")
+
+@Term("Version")
+@ReadOnly
+def lunaix_ver():
+ """
+ Lunaix kernel version
+ """
+
+ type(str)
+
+ seq_num = int(time.time() / 3600)
+ default("dev-2024_%d"%(seq_num))
+
+@Collection
+def debug_and_testing():
+ """
+ General settings for kernel debugging feature
+ """
+
+ @Term("Supress assertion")
+ def no_assert():
+ """
+ Supress all assertion fail activity.
+ Note: Enable this is highly NOT recommended and would result system
+ extermly unstable
+ """
+ type(bool)
+ default(False)
\ No newline at end of file
--- /dev/null
+use({
+ config("arch"): {
+ "i386": "i386",
+ "aarch64": "arm",
+ "rv64": "riscv"
+ }
+})
\ No newline at end of file
--- /dev/null
+include("i386/LConfig")
+
+@Collection
+def architecture_support():
+ """
+ Config ISA related features
+ """
+
+ @Term
+ def arch():
+ """ Config ISA support """
+ type(["i386", "x86_64", "aarch64", "rv64"])
+ default("i386")
--- /dev/null
+use("hal")
+
+sources([
+ "exceptions/interrupts.c",
+ "exceptions/i386_isrdef.c",
+ "exceptions/intr_routines.c",
+ "exceptions/i386_isrm.c",
+
+ "exceptions/interrupt.S",
+ "exceptions/intrhnds.S",
+])
+
+sources([
+ "boot/mb_parser.c",
+ "boot/kpt_setup.c",
+ "boot/init32.c",
+
+ "boot/boot.S",
+ "boot/prologue.S"
+])
+
+sources([
+ "mm/fault.c",
+ "mm/tlb.c",
+ "mm/pmm.c",
+ "mm/gdt.c",
+ "mm/vmutils.c"
+])
+
+sources([
+ "klib/fast_crc.c",
+ "klib/fast_str.c",
+ "hart.c",
+ "arch.c",
+ "gdbstub.c",
+ "trace.c",
+
+ "syscall.S",
+ "failsafe.S"
+])
+
+headers([
+ "includes"
+])
\ No newline at end of file
--- /dev/null
+
+@Group
+def x86_configurations():
+ @Term
+ def x86_enable_sse_feature():
+ """
+ Config whether to allow using SSE feature for certain
+ optimization
+ """
+
+ type(bool)
+ default(False)
+
+ add_to_collection(architecture_support)
+
+
+ @Term
+ def x86_boot_options():
+ type(["multiboot"])
+ # type(["multiboot", "none"])
+ default("multiboot")
+
+ return v(arch) == "i386"
\ No newline at end of file
--- /dev/null
+sources([
+ "apic.c",
+ "rngx86.c",
+ "cpu.c",
+ "ps2kbd.c",
+ "apic_timer.c",
+ "ioapic.c",
+ "mc146818a.c"
+])
\ No newline at end of file
#include <lunaix/types.h>
#include <klibc/crc.h>
-#ifdef CONFIG_X86_SSE4
+#ifdef CONFIG_X86_ENABLE_SSE_FEATURE
unsigned int
crc32b(unsigned char* data, unsigned int size)
{
+++ /dev/null
-#!/usr/bin/bash
-
-cat GRUB_TEMPLATE | envsubst > "$1"
\ No newline at end of file
+++ /dev/null
-#ifndef __LUNAIX_CONFIG_H
-#define __LUNAIX_CONFIG_H
-
-// #define CONFIG_PMALLOC_BUDDY
-// #define CONFIG_PMALLOC_NCONTIG
-#define CONFIG_PMALLOC_SIMPLE
-
-#define CONFIG_PMALLOC_SIMPLE_PO0_THRES 4096
-#define CONFIG_PMALLOC_SIMPLE_PO1_THRES 2048
-#define CONFIG_PMALLOC_SIMPLE_PO2_THRES 2048
-#define CONFIG_PMALLOC_SIMPLE_PO3_THRES 2048
-#define CONFIG_PMALLOC_SIMPLE_PO4_THRES 512
-#define CONFIG_PMALLOC_SIMPLE_PO5_THRES 512
-#define CONFIG_PMALLOC_SIMPLE_PO6_THRES 128
-#define CONFIG_PMALLOC_SIMPLE_PO7_THRES 128
-#define CONFIG_PMALLOC_SIMPLE_PO8_THRES 64
-#define CONFIG_PMALLOC_SIMPLE_PO9_THRES 16
-
-#endif /* __LUNAIX_CONFIG_H */
+++ /dev/null
-#ifndef __LUNAIX_FLAGS_H
-#define __LUNAIX_FLAGS_H
-
-#if __ARCH__ == i386
-#define PLATFORM_TARGET "x86_32"
-#else
-#define PLATFORM_TARGET "unknown"
-#endif
-
-#define LUNAIX_VER "0.0.1-dev"
-
-/*
- Uncomment below to disable all assertion
-*/
-// #define __LUNAIXOS_NASSERT__
-
-#endif /* __LUNAIX_FLAGS_H */
--- /dev/null
+use("acpi")
+use("ahci")
+use("char")
+use("gfxa")
+use("rtc")
+use("term")
+use("timer")
+use("bus")
+
--- /dev/null
+sources([
+ "parser/madt_parser.c",
+ "parser/mcfg_parser.c",
+ "acpi.c"
+])
\ No newline at end of file
--- /dev/null
+sources([
+ "ahci_pci.c",
+ "hbadev_export.c",
+ "ahci.c",
+ "utils.c",
+ "io_event.c",
+ "atapi.c",
+ "ata.c"
+])
\ No newline at end of file
--- /dev/null
+sources([
+ "pci.c"
+])
\ No newline at end of file
--- /dev/null
+use("uart")
+
+sources([
+ "devnull.c",
+ "serial.c",
+ "devzero.c",
+ "lxconsole.c",
+])
\ No newline at end of file
--- /dev/null
+sources([
+ "16550_base.c",
+ "16550_pmio.c"
+])
\ No newline at end of file
--- /dev/null
+use("vga")
+
+sources([
+ "gfxm.c"
+])
\ No newline at end of file
--- /dev/null
+sources([
+ "vga_pmio_ops.c",
+ "vga_gfxm_ops.c",
+ "vga.c",
+ "vga_mmio_ops.c",
+ "vga_pci.c"
+])
\ No newline at end of file
--- /dev/null
+sources([
+ "rtc_device.c"
+])
\ No newline at end of file
--- /dev/null
+sources([
+ "term.c",
+ "console.c",
+ "term_io.c",
+ "lcntls/ansi_cntl.c",
+ "lcntls/lcntl.c",
+])
\ No newline at end of file
--- /dev/null
+sources([
+ "timer_device.c"
+])
\ No newline at end of file
struct ppage* pool_start;
struct ppage* pool_end;
-#if defined(CONFIG_PMALLOC_NCONTIG)
+#if defined(CONFIG_PMALLOC_METHOD_NCONTIG)
struct llist_header idle_page;
struct llist_header busy_page;
-#elif defined(CONFIG_PMALLOC_BUDDY)
+#elif defined(CONFIG_PMALLOC_METHOD_BUDDY)
struct llist_header idle_order[MAX_PAGE_ORDERS];
-#elif defined(CONFIG_PMALLOC_SIMPLE)
+#elif defined(CONFIG_PMALLOC_METHOD_SIMPLE)
struct llist_header idle_order[MAX_PAGE_ORDERS];
int count[MAX_PAGE_ORDERS];
: 0) \
: (31 - clz(x)))
-#ifndef __LUNAIXOS_NASSERT__
+#ifndef CONFIG_NO_ASSERT
#define assert(cond) \
do { \
if (unlikely(!(cond))) { \
#define assert(cond) (void)(cond); // assert nothing
#define assert_msg(cond, msg) (void)(cond); // assert nothing
-#endif // __LUNAIXOS_NASSERT__
+#endif // CONFIG_NO_ASSERT
void noret
panick(const char* msg);
#include <lunaix/hart_state.h>
#include <hal/serial.h>
+#include <sys/gdbstub.h>
struct gdb_state
{
include os.mkinc
include toolchain.mkinc
-kexclusion = $(shell cat ksrc.excludes)
-
-define ksrc_dirs
- kernel
- hal
- libs
- arch/$(ARCH)
-endef
-
-define kinc_dirs
- includes
- includes/usr
- arch/$(ARCH)/includes
-endef
-
+ksrc_files = $(shell cat .builder/sources.list)
+kinc_dirs = $(shell cat .builder/includes.list)
+khdr_files = $(shell cat .builder/headers.list)
+khdr_files += .builder/configs.h
kbin_dir := $(BUILD_DIR)
kbin := $(BUILD_NAME)
-ksrc_files := $(foreach f, $(ksrc_dirs), $(shell find $(f) -name "*.[cS]"))
-ksrc_files := $(filter-out $(kexclusion),$(ksrc_files))
ksrc_objs := $(addsuffix .o,$(ksrc_files))
ksrc_deps := $(addsuffix .d,$(ksrc_files))
-
+khdr_opts := $(addprefix -include ,$(khdr_files))
kinc_opts := $(addprefix -I,$(kinc_dirs))
tmp_kbin := $(BUILD_DIR)/tmpk.bin
ksymtable := lunaix_ksyms.o
-CFLAGS += -include flags.h
-CFLAGS += -include config.h
+CFLAGS += $(khdr_opts)
+
+-include $(ksrc_deps)
-%.S.o: %.S
+%.S.o: %.S $(khdr_files) kernel.mk
$(call status_,AS,$<)
- @$(CC) $(CFLAGS) $(kinc_opts) -c $< -o $@
+ @$(CC) $(CFLAGS) $(kinc_opts) -MMD -MP -c $< -o $@
-%.c.o: %.c
+%.c.o: %.c $(khdr_files) kernel.mk
$(call status_,CC,$<)
- @$(CC) $(CFLAGS) $(kinc_opts) -c $< -o $@
+ @$(CC) $(CFLAGS) $(kinc_opts) -MMD -MP -c $< -o $@
$(tmp_kbin): $(ksrc_objs)
$(call status_,LD,$@)
--- /dev/null
+use("block")
+use("debug")
+use("device")
+use("ds")
+use("exe")
+use("fs")
+use("mm")
+use("process")
+
+sources([
+ "boot_helper.c",
+ "kcmd.c",
+ "kinit.c",
+ "lunad.c",
+ "spike.c",
+ "tty/tty.c",
+ "kprint/kp_records.c",
+ "kprint/kprintf.c",
+ "time/clock.c",
+ "time/timer.c"
+])
\ No newline at end of file
--- /dev/null
+@Collection
+def kernel_feature():
+ """ Config kernel features """
+ pass
+
+include("mm/LConfig")
\ No newline at end of file
--- /dev/null
+sources([
+ "blkpart_gpt.c",
+ "blk_mapping.c",
+ "blkio.c",
+ "block.c"
+])
\ No newline at end of file
--- /dev/null
+sources([
+ "failsafe.c",
+ "trace.c",
+ # "gdbstub.c"
+])
\ No newline at end of file
#include <sys/port_io.h>
#include <sys/cpu.h>
-#include <sys/gdbstub.h>
/*****************************************************************************
* Macros
--- /dev/null
+sources([
+ "device.c",
+ "capability.c",
+ "devdb.c",
+ "devfs.c",
+ "input.c",
+ "poll.c"
+])
--- /dev/null
+sources([
+ "waitq.c",
+ "buffer.c",
+ "lru.c",
+ "rbuffer.c",
+ "btrie.c",
+ "semaphore.c",
+ "mutex.c",
+ "hstr.c",
+ "fifo.c",
+ "rwlock.c"
+])
\ No newline at end of file
--- /dev/null
+use("elf32")
+
+sources([
+ "exec.c"
+])
\ No newline at end of file
--- /dev/null
+sources([
+ "elf32bfmt.c",
+ "ldelf32.c"
+])
\ No newline at end of file
--- /dev/null
+use("twifs")
+use("ramfs")
+use("iso9660")
+
+sources([
+ "twimap.c",
+ "pcache.c",
+ "mount.c",
+ "xattr.c",
+ "vfs.c",
+ "defaults.c",
+ "path_walk.c",
+ "fsm.c",
+ "fs_export.c",
+ "probe_boot.c"
+])
\ No newline at end of file
__version_rd(struct twimap* map)
{
twimap_printf(map,
- "LunaixOS version %s (%s-gnu-gcc %s) %s %s",
- LUNAIX_VER,
- PLATFORM_TARGET,
+ "LunaixOS version %s (gnu-cc %s) %s %s",
+ CONFIG_LUNAIX_VER,
__VERSION__,
__DATE__,
__TIME__);
--- /dev/null
+sources([
+ "inode.c",
+ "file.c",
+ "mount.c",
+ "directory.c",
+ "utils.c",
+ "rockridge.c"
+])
\ No newline at end of file
--- /dev/null
+sources([
+ "ramfs.c"
+])
\ No newline at end of file
--- /dev/null
+sources([
+ "twifs.c"
+])
\ No newline at end of file
--- /dev/null
+sources([
+ "mmap.c",
+ "valloc.c",
+ "cake.c",
+ "fault.c",
+ "procvm.c",
+ "page.c",
+ "region.c",
+ "pmalloc_simple.c",
+ "vmm.c",
+ "mmio.c",
+ "pmm.c",
+ "cake_export.c",
+ "vmap.c",
+ "dmm.c"
+])
\ No newline at end of file
--- /dev/null
+
+@Collection
+def memory_subsystem():
+ """ Config the memory subsystem """
+
+ @Collection
+ def physical_mm():
+ """ Physical memory manager """
+
+ @Term
+ def pmalloc_method():
+ """ Allocation policy for phiscal memory """
+
+ type(["simple", "buddy", "ncontig"])
+ default("simple")
+
+ @Group
+ def pmalloc_simple_po_thresholds():
+
+ @Term
+ def pmalloc_simple_max_po0():
+ """ free list capacity for order-0 pages """
+
+ type(int)
+ default(4096)
+
+ @Term
+ def pmalloc_simple_max_po1():
+ """ free list capacity for order-1 pages """
+
+ type(int)
+ default(2048)
+
+ @Term
+ def pmalloc_simple_max_po2():
+ """ free list capacity for order-2 pages """
+
+ type(int)
+ default(2048)
+
+ @Term
+ def pmalloc_simple_max_po3():
+ """ free list capacity for order-3 pages """
+
+ type(int)
+ default(2048)
+
+ @Term
+ def pmalloc_simple_max_po4():
+ """ free list capacity for order-4 pages """
+
+ type(int)
+ default(512)
+
+ @Term
+ def pmalloc_simple_max_po5():
+ """ free list capacity for order-5 pages """
+
+ type(int)
+ default(512)
+
+ @Term
+ def pmalloc_simple_max_po6():
+ """ free list capacity for order-6 pages """
+
+ type(int)
+ default(128)
+
+ @Term
+ def pmalloc_simple_max_po7():
+ """ free list capacity for order-7 pages """
+
+ type(int)
+ default(128)
+
+ @Term
+ def pmalloc_simple_max_po8():
+ """ free list capacity for order-8 pages """
+
+ type(int)
+ default(64)
+
+ @Term
+ def pmalloc_simple_max_po9():
+ """ free list capacity for order-9 pages """
+
+ type(int)
+ default(32)
+
+ return v(pmalloc_method) == "simple"
+
+ add_to_collection(kernel_feature)
\ No newline at end of file
#include <lunaix/spike.h>
#include "pmm_internal.h"
-#ifdef CONFIG_PMALLOC_SIMPLE
+#ifdef CONFIG_PMALLOC_METHOD_SIMPLE
// Simple PM Allocator (segregated next fit)
#define INIT_FLAG 0b10
static const int po_limit[] = {
- CONFIG_PMALLOC_SIMPLE_PO0_THRES,
- CONFIG_PMALLOC_SIMPLE_PO1_THRES,
- CONFIG_PMALLOC_SIMPLE_PO2_THRES,
- CONFIG_PMALLOC_SIMPLE_PO3_THRES,
- CONFIG_PMALLOC_SIMPLE_PO4_THRES,
- CONFIG_PMALLOC_SIMPLE_PO5_THRES,
- CONFIG_PMALLOC_SIMPLE_PO6_THRES,
- CONFIG_PMALLOC_SIMPLE_PO7_THRES,
- CONFIG_PMALLOC_SIMPLE_PO8_THRES,
- CONFIG_PMALLOC_SIMPLE_PO9_THRES,
+ CONFIG_PMALLOC_SIMPLE_MAX_PO0,
+ CONFIG_PMALLOC_SIMPLE_MAX_PO1,
+ CONFIG_PMALLOC_SIMPLE_MAX_PO2,
+ CONFIG_PMALLOC_SIMPLE_MAX_PO3,
+ CONFIG_PMALLOC_SIMPLE_MAX_PO4,
+ CONFIG_PMALLOC_SIMPLE_MAX_PO5,
+ CONFIG_PMALLOC_SIMPLE_MAX_PO6,
+ CONFIG_PMALLOC_SIMPLE_MAX_PO7,
+ CONFIG_PMALLOC_SIMPLE_MAX_PO8,
+ CONFIG_PMALLOC_SIMPLE_MAX_PO9,
};
static inline bool
--- /dev/null
+sources([
+ "signal.c",
+ "sched.c",
+ "fork.c",
+ "process.c",
+ "taskfs.c",
+ "task_attr.c",
+ "thread.c"
+])
\ No newline at end of file
+++ /dev/null
-kernel/debug/sdbg.c
-kernel/debug/gdbstub.c
\ No newline at end of file
--- /dev/null
+sources([
+ "crc.c",
+ "hash.c",
+ "klibc/itoa.c",
+ "klibc/ksprintf.c",
+ "klibc/string/mem.c",
+ "klibc/string/strchr.c",
+ "klibc/string/strcmp.c",
+ "klibc/string/strcpy.c",
+ "klibc/string/strlen.c",
+ "klibc/string/trim.c"
+])
\ No newline at end of file
#include <lunaix/compiler.h>
unsigned long weak
-strlen(const char* str)
+strlen(const char *str)
{
unsigned long len = 0;
while (str[len])
}
unsigned long weak
-strnlen(const char* str, unsigned long max_len)
+strnlen(const char *str, unsigned long max_len)
{
unsigned long len = 0;
while (str[len] && len <= max_len)
echo "failed" && exit 1;\
fi
-check-cc:
- @echo -n "checking target i686-elf.... "
- @test "`i686-elf-gcc -dumpmachine`" = "i686-elf" && echo ok || (echo "failed" && exit 1)
+define builder_data
+ .builder/sources.list
+ .builder/headers.list
+ .builder/includes.list
+endef
+
+all_lconfigs = $(shell find . -name "LConfig")
$(kbuild_dir):
@mkdir -p $(kbin_dir)
@mkdir -p $(os_img_dir)/boot/grub
@mkdir -p $(os_img_dir)/usr
+.builder/configs.h: $(all_lconfigs)
+ @echo restarting configuration...
+ @echo
+ @./scripts/build-tools/luna_build.py --config --lconfig-file LConfig -o $(@D)
+
+.builder/%.list: .builder/configs.h
+ @./scripts/build-tools/luna_build.py LBuild --lconfig-file LConfig -o $(@D)
+
.PHONY: kernel
export BUILD_DIR=$(kbin_dir)
export BUILD_NAME=$(kbin)
-kernel:
+kernel: $(builder_data)
$(call status,TASK,$(notdir $@))
@$(MAKE) $(MKFLAGS) -I $(mkinc_dir) -f kernel.mk all
export _OS_NAME=$(OS_NAME)
image: usr/build kernel
$(call status,TASK,$(notdir $@))
- @./config-grub.sh $(os_img_dir)/boot/grub/grub.cfg
+ @./scripts/grub/config-grub.sh $(os_img_dir)/boot/grub/grub.cfg
@cp -r usr/build/* $(os_img_dir)/usr
@cp -r $(kbin_dir)/* $(os_img_dir)/boot
- @grub-mkrescue -o $(kimg) $(os_img_dir) -- -volid "$(OS_ID) $(OS_VER)" -system_id "$(OS_NAME)"
+ @grub-mkrescue -o $(kimg) $(os_img_dir)\
+ -- -volid "$(OS_ID) $(OS_VER)" -system_id "$(OS_NAME)"
usr/build: user
-check: $(DEPS) check-cc GRUB_TEMPLATE
+check: $(DEPS) check-cc scripts/grub/GRUB_TEMPLATE
prepare: check $(os_img_dir)
all-debug: bootable-debug
clean:
- @rm -rf $(kbuild_dir) || exit 1
@$(MAKE) -C usr clean -I $(mkinc_dir)
@$(MAKE) -f kernel.mk clean -I $(mkinc_dir)
+ @rm -rf $(kbuild_dir) || exit 1
+ @rm -f .builder/*.list || exit 1
-run: $(BUILD_DIR)/$(OS_ISO)
- @qemu-system-i386 $(QEMU_OPTIONS)
+run: all
+ @qemu-system-i386 $(call get_qemu_options,$(kimg))
@sleep 1
@telnet 127.0.0.1 $(QEMU_MON_PORT)
--- /dev/null
+# LunaBuild
+
+LunaBuild is programmable source file selection tool. It does not build things
+by itself, rather, it selects which source file should be feed as input to
+other tranditional build tools such as make. The selection logic is completely
+programmable and convey through `LBuild` file which is essentially a python
+script. As the primary design goal for LunaBuild is simple, lightweight and
+standalone. It just plain python, with some extra predefined functions and
+automatic variables, so does not force user to learn some other weird domain
+specific language (yes, that's you, CMake!).
+
+## Usage
+
+Invoke `./luna_build.py <root_lbuild> -o <out_dir>`. It will output two file
+to the `<out_dir>`: `sources.list` and `headers.list`. Contains all the source
+files and header files to be used by the build process.
+
+## Core Functions
+
+LunaBuild provide following function to help user select source files.
+
+### [func] `use(lbuild_path)`
+
+Include another LBuild file. `lbuild_path` is the path relative to current
+directory, pointed to the file. It can be also pointed to a directory, for
+which the LBuild file is inferred as `$lbuild_path/LBuild`.
+
+For example:
+
+```py
+use("dir")
+use("dir/LBuild")
+```
+
+both are equivalent.
+
+### [func] `sources(src_list)`
+
+Select a list of source files, all paths used are relative to current
+directory. For example,
+
+```py
+sources([ "a.c", "b.c", "c.c" ])
+```
+
+### [func] `headers(src_list)`
+
+Select a list of header file or include directory, all paths used are
+relative to current directory. For example,
+
+```py
+headers([ "includes/", "includes/some.h" ])
+```
+
+### [func] `configured(name)`
+
+Check whether a configuration is present, the name for the configuration
+is defined by external configuration provider.
+
+### [func] `config(name)`
+
+Read the value of a configuration, raise exception is not exists.
+
+### [var] `_script`
+
+Automatic variable, a path to the current build file.
+
+## Short-hands
+
+LunaBuild provide some useful short-hand notations
+
+### Conditional Select
+
+Suppose you have two source files `src_a.c` and `src_b.c`, for which
+their selection will be depends on the value of a configuration
+`WHICH_SRC`. A common way is to use `if-else` construct
+
+```py
+if config("WHICH_SRC") == "select_a":
+ sources("src_a.c")
+elif config("WHICH_SRC") == "select_b":
+ sources("src_b.c")
+```
+
+LunaBuild allow you to short hand it as such:
+
+```py
+sources({
+ config("WHICH_SRC"): {
+ "select_a": "src_a.c",
+ "select_b": "src_b.c",
+ # more...
+ }
+})
+```
+
+It can also be extended easily for multiple choices and allow nesting.
+
+You may also notice we no longer wrap the parameter with square bracket,
+this is also another short-hand, the array notation is not needed when
+there is only one element to add.
--- /dev/null
+from lcfg.api import ConfigIOProvider
+from lcfg.utils import is_basic
+
+import re
+
+class CHeaderConfigProvider(ConfigIOProvider):
+ def __init__(self, header_save,
+ header_prefix="CONFIG_") -> None:
+ self.__header_export = header_save
+ self.__prefix = header_prefix
+ self.__re = re.compile(r"^[A-Za-z0-9_]+$")
+
+ def export(self, env, config_dict):
+ lines = []
+ for k, v in config_dict.items():
+ result = [ "#define" ]
+ s = str.upper(k)
+ s = f"{self.__prefix}{s}"
+
+ if isinstance(v, str) and self.__re.match(v):
+ s = f"{s}_{str.upper(v)}"
+ v = ""
+
+ result.append(s)
+
+ v = self.serialize_value(v)
+ if v is None or v is False:
+ result.insert(0, "//")
+ else:
+ result.append(v)
+
+ lines.append(" ".join(result))
+
+ with open(self.__header_export, 'w') as f:
+ f.write("\n".join(lines))
+
+
+ def serialize_value(self, v):
+ if v is None:
+ return None
+
+ if isinstance(v, bool):
+ return v
+
+ if v and isinstance(v, str):
+ return f'"{v}"'
+
+ if is_basic(v):
+ return str(v)
+
+ raise ValueError(
+ f"serialising {type(v)}: not supported")
--- /dev/null
+from lbuild.api import ConfigProvider
+from lcfg.common import LConfigEnvironment
+
+class LConfigProvider(ConfigProvider):
+ def __init__(self, lcfg_env: LConfigEnvironment) -> None:
+ super().__init__()
+ self.__env = lcfg_env
+
+ def configured_value(self, name):
+ return self.__env.lookup_value(name)
+
+ def has_config(self, name):
+ try:
+ self.__env.lookup_value(name)
+ return True
+ except:
+ return False
\ No newline at end of file
--- /dev/null
+from lcfg.api import (
+ RenderContext,
+ InteractiveRenderer,
+ Renderable,
+ ConfigTypeCheckError,
+ ConfigLoadException
+)
+from lcfg.common import LConfigEnvironment
+
+import shlex
+from lib.utils import join_path
+from pathlib import Path
+import readline
+
+class ShellException(Exception):
+ def __init__(self, *args: object) -> None:
+ super().__init__(*args)
+
+class ViewElement:
+ def __init__(self, label, node) -> None:
+ self.label = label
+ self.node = node
+
+ def expand(self, sctx):
+ return False
+
+ def read(self, sctx):
+ return None
+
+ def write(self, sctx, val):
+ return None
+
+ def get_help(self, sctx):
+ return self.node.help_prompt()
+
+ def get_type(self, sctx):
+ return "N/A"
+
+ def draw(self, sctx, canvas):
+ pass
+
+
+class SubviewLink(ViewElement):
+ def __init__(self, label, node, cb) -> None:
+ super().__init__(label, node)
+ self.__callback = cb
+
+ def expand(self, sctx):
+ sctx.clear_view()
+ self.__callback(sctx)
+ return True
+
+ def draw(self, sctx, canvas):
+ print(f" [{self.label}]")
+
+ def get_type(self, sctx):
+ return f"Collection: {self.label}"
+
+class RootWrapper(ViewElement):
+ def __init__(self, root) -> None:
+ self.__root = root
+ super().__init__("root", None)
+
+ def expand(self, sctx):
+ sctx.clear_view()
+ self.__root.render(sctx)
+ return True
+
+ def draw(self, sctx, canvas):
+ pass
+
+ def get_type(self, sctx):
+ return ""
+
+class FieldView(ViewElement):
+ def __init__(self, label, node) -> None:
+ super().__init__(label, node)
+
+ def read(self, sctx):
+ return self.node.get_value()
+
+ def write(self, sctx, val):
+ if self.node.read_only():
+ return None
+
+ self.node.set_value(val)
+ return val
+
+ def get_type(self, sctx):
+ return f"Config term: {self.label}\n{self.node.get_type()}"
+
+ def draw(self, sctx, canvas):
+ suffix = ""
+ if self.node.read_only():
+ suffix = " (ro)"
+ print(f" {self.label}{suffix}")
+
+class ShellContext(RenderContext):
+ def __init__(self) -> None:
+ super().__init__()
+
+ self._view = {}
+ self._subviews = []
+ self._field = []
+
+ def add_expandable(self, label, node, on_expand_cb):
+ name = node.get_name()
+ self._view[name] = SubviewLink(name, node, on_expand_cb)
+ self._subviews.append(name)
+
+ def add_field(self, label, node):
+ name = node.get_name()
+ self._view[name] = FieldView(name, node)
+ self._field.append(name)
+
+ def clear_view(self):
+ self._view.clear()
+ self._subviews.clear()
+ self._field.clear()
+
+ def redraw(self):
+ for v in self._subviews + self._field:
+ self._view[v].draw(self, None)
+
+ def get_view(self, label):
+ if label in self._view:
+ return self._view[label]
+ return None
+
+class InteractiveShell(InteractiveRenderer):
+ def __init__(self, root: Renderable) -> None:
+ super().__init__()
+ self.__levels = [RootWrapper(root)]
+ self.__aborted = True
+ self.__sctx = ShellContext()
+ self.__cmd = {
+ "ls": (
+ "list all config node under current collection",
+ lambda *args:
+ self.__sctx.redraw()
+ ),
+ "help": (
+ "print help prompt for given name of node",
+ lambda *args:
+ self.print_help(*args)
+ ),
+ "type": (
+ "print the type of a config node",
+ lambda *args:
+ self.print_type(*args)
+ ),
+ "cd": (
+ "navigate to a collection node given a unix-like path",
+ lambda *args:
+ self.get_in(*args)
+ ),
+ "usage": (
+ "print out the usage",
+ lambda *args:
+ self.print_usage()
+ )
+ }
+
+ def get_path(self):
+ l = [level.label for level in self.__levels[1:]]
+ return f"/{'/'.join(l)}"
+
+ def resolve(self, relative):
+ ctx = ShellContext()
+ p = join_path(self.get_path(), relative)
+ ps = Path(p).resolve().parts
+ if ps[0] == '/':
+ ps = ps[1:]
+
+ node = self.__levels[0]
+ levels = [node]
+ for part in ps:
+ if not node.expand(ctx):
+ raise ShellException(f"node is not a collection: {part}")
+
+ node = ctx.get_view(part)
+ if not node:
+ raise ShellException(f"no such node: {part}")
+
+ levels.append(node)
+
+ return (node, levels)
+
+ def print_usage(self):
+ for cmd, (desc, _) in self.__cmd.items():
+ print("\n".join([
+ cmd,
+ f" {desc}",
+ ""
+ ]))
+
+ def print_help(self, node_name):
+ view, _ = self.resolve(node_name)
+
+ print(view.get_help(self.__sctx))
+
+ def print_type(self, node_name):
+ view, _ = self.resolve(node_name)
+
+ print(view.get_type(self.__sctx))
+
+ def do_read(self, node_name):
+ view, _ = self.resolve(node_name)
+ rd_val = view.read(self.__sctx)
+ if not rd_val:
+ raise ShellException(f"config node {view.label} is not readable")
+
+ print(rd_val)
+
+ def do_write(self, node_name, val):
+ view, _ = self.resolve(node_name)
+ wr = view.write(self.__sctx, val)
+ if not wr:
+ raise ShellException(f"config node {view.label} is read only")
+
+ print(f"write: {val}")
+ self.do_render()
+
+ def get_in(self, node_name):
+ view, lvl = self.resolve(node_name)
+
+ if not view.expand(self.__sctx):
+ print(f"{node_name} is not a collection")
+ return
+
+ self.__levels = lvl
+
+ def do_render(self):
+
+ curr = self.__levels[-1]
+ curr.expand(self.__sctx)
+
+ def __loop(self):
+ prefix = "config: %s> "%(self.__levels[-1].label)
+
+ line = input(prefix)
+ args = shlex.split(line)
+ if not args:
+ return True
+
+ cmd = args[0]
+ if cmd in self.__cmd:
+ self.__cmd[cmd][1](*args[1:])
+ return True
+
+ if cmd == "exit":
+ self.__aborted = False
+ return False
+
+ if cmd == "abort":
+ return False
+
+ node = self.resolve(cmd)
+ if not node:
+ raise ShellException(f"unrecognised command {line}")
+
+ if len(args) == 3 and args[1] == '=':
+ self.do_write(args[0], args[2])
+ return True
+
+ if len(args) == 1:
+ self.do_read(args[0])
+ return True
+
+ print(f"unrecognised command {line}")
+ return True
+
+
+ def render_loop(self):
+ self.do_render()
+ print("\n".join([
+ "Interactive LConfig Shell",
+ " type 'usage' to find out how to use",
+ " type 'exit' to end (with saving)",
+ " type 'abort' to abort (without saving)",
+ " type node name directly to read the value",
+ " type 'node = val' to set 'val' to 'node'",
+ ""
+ ]))
+ while True:
+ try:
+ if not self.__loop():
+ break
+ except KeyboardInterrupt:
+ break
+ except ConfigTypeCheckError as e:
+ print(e.args[0])
+ except ShellException as e:
+ print(e.args[0])
+
+ return not self.__aborted
\ No newline at end of file
--- /dev/null
+class ConfigProvider:
+ def __init__(self) -> None:
+ pass
+
+ def configured_value(self, name):
+ raise ValueError(f"config '{name}' is undefined or disabled")
+
+ def has_config(self, name):
+ return False
\ No newline at end of file
--- /dev/null
+from lib.utils import join_path
+import os
+
+class BuildEnvironment:
+ def __init__(self, workspace_dir) -> None:
+ self.__config_provider = None
+ self.__sources = []
+ self.__headers = []
+ self.__inc_dir = []
+ self.__ws_dir = workspace_dir
+
+ def set_config_provider(self, provider):
+ self.__config_provider = provider
+
+ def config_provider(self):
+ return self.__config_provider
+
+ def add_sources(self, sources):
+ self.__sources += sources
+
+ def add_headers(self, sources):
+ for h in sources:
+ if not os.path.isdir(h):
+ self.__headers.append(h)
+ else:
+ self.__inc_dir.append(h)
+
+ def to_wspath(self, file):
+ path = join_path(self.__ws_dir, file)
+ return os.path.relpath(path, self.__ws_dir)
+
+ def export(self, out_dir):
+ path = os.path.join(out_dir, "sources.list")
+ with open(path, "w") as f:
+ f.write("\n".join(self.__sources))
+
+ path = os.path.join(out_dir, "headers.list")
+ with open(path, "w") as f:
+ f.write("\n".join(self.__headers))
+
+ path = os.path.join(out_dir, "includes.list")
+ with open(path, "w") as f:
+ f.write("\n".join(self.__inc_dir))
\ No newline at end of file
--- /dev/null
+from lib.sandbox import Sandbox
+from lib.utils import join_path
+from .common import BuildEnvironment
+
+import os
+
+class LunaBuildFile(Sandbox):
+ def __init__(self, env: BuildEnvironment, path) -> None:
+ super().__init__({
+ "_script":
+ path,
+ "sources":
+ lambda src: self.export_sources(src),
+ "headers":
+ lambda hdr: self.export_headers(hdr),
+ "configured":
+ lambda name: self.check_config(name),
+ "config":
+ lambda name: self.read_config(name),
+ "use":
+ lambda file: self.import_buildfile(file)
+ })
+
+ self.__srcs = []
+ self.__hdrs = []
+ self.__env = env
+
+ self.__path = path
+ self.__dir = os.path.dirname(path)
+
+ def resolve(self):
+ self.execute(self.__path)
+ self.__env.add_sources(self.__do_process(self.__srcs))
+ self.__env.add_headers(self.__do_process(self.__hdrs))
+
+ def __do_process(self, list):
+ resolved = []
+ for entry in list:
+ if not entry:
+ continue
+ resolved.append(self.__resolve_value(entry))
+ return resolved
+
+ def expand_select(self, val):
+ tests = list(val.keys())
+ if len(tests) != 1:
+ raise TypeError(
+ "select statement must have exactly one conditional")
+
+ test = tests[0]
+ outcomes = val[test]
+ if test not in outcomes:
+ self.__raise("unbounded select")
+ return outcomes[test]
+
+ def __resolve_value(self, source):
+ resolved = source
+ while not isinstance(resolved, str):
+ if isinstance(resolved, dict):
+ resolved = self.expand_select(resolved)
+ else:
+ self.__raise(f"entry with unknown type: {resolved}")
+
+ resolved = resolved.strip()
+ resolved = join_path(self.__dir, resolved)
+
+ return self.__env.to_wspath(resolved)
+
+ def import_buildfile(self, path):
+ path = self.__resolve_value(path)
+ path = self.__env.to_wspath(path)
+
+ if (os.path.isdir(path)):
+ path = os.path.join(path, "LBuild")
+
+ if not os.path.exists(path):
+ self.__raise("Build file not exist: %s", path)
+
+ if os.path.abspath(path) == os.path.abspath(self.__path):
+ self.__raise("self dependency detected")
+
+ LunaBuildFile(self.__env, path).resolve()
+
+ def export_sources(self, src):
+ if not isinstance(src, list):
+ src = [src]
+ self.__srcs += src
+
+ def export_headers(self, hdr):
+ if not isinstance(hdr, list):
+ hdr = [hdr]
+ self.__hdrs += hdr
+
+ def check_config(self, name):
+ return self.__env.config_provider().has_config(name)
+
+ def read_config(self, name):
+ return self.__env.config_provider().configured_value(name)
+
+ def __raise(self, msg, *kargs):
+ raise Exception(msg%kargs)
\ No newline at end of file
--- /dev/null
+from abc import abstractmethod
+
+class ConfigLoadBaseException(Exception):
+ def __init__(self, msg, node, inner=None) -> None:
+ super().__init__(msg)
+
+ self.__msg = msg
+ self.__node = node
+ self.__inner = inner
+
+ def __str__(self) -> str:
+ return super().__str__()
+
+class ConfigLoadException(ConfigLoadBaseException):
+ def __init__(self, msg, node = None, inner=None) -> None:
+ super().__init__(msg, node, inner)
+
+class ConfigTypeCheckError(ConfigLoadBaseException):
+ def __init__(self, msg, node, inner=None) -> None:
+ super().__init__(msg, node, inner)
+
+class ConfigIOProvider:
+ def __init__(self) -> None:
+ pass
+
+ @abstractmethod
+ def export(self, env, config_dict):
+ pass
+
+ @abstractmethod
+ def save(self, env, config_dict):
+ pass
+
+ @abstractmethod
+ def load(self, env):
+ pass
+
+
+class TypeProviderBase:
+ def __init__(self, type) -> None:
+ self._type = type
+
+ @abstractmethod
+ def check(self, val):
+ return True
+
+ @abstractmethod
+ def serialise(self, val):
+ pass
+
+ @abstractmethod
+ def deserialise(self, val):
+ pass
+
+ @abstractmethod
+ def parse_input(self, input_str):
+ pass
+
+ @abstractmethod
+ def to_input(self, val):
+ pass
+
+ def allow_none(self):
+ return False
+
+ @staticmethod
+ def typedef_matched(typedef):
+ return False
+
+ @abstractmethod
+ def __str__(self) -> str:
+ pass
+
+class Renderable:
+ def __init__(self) -> None:
+ pass
+
+ @abstractmethod
+ def render(self, rctx):
+ pass
+
+class RenderContext:
+ def __init__(self) -> None:
+ pass
+
+ @abstractmethod
+ def add_expandable(self, label, node, on_expand_cb):
+ pass
+
+ @abstractmethod
+ def add_field(self, label, node):
+ pass
+
+class InteractiveRenderer(RenderContext):
+ def __init__(self) -> None:
+ super().__init__()
+
+ @abstractmethod
+ def render_loop(self):
+ pass
+
+
+class LConfigBuiltin:
+ def __init__(self, f, is_contextual, rename=None, caller_type=[]):
+ self.name = f.__name__ if not rename else rename
+ self.__f = f
+ self.__contextual = is_contextual
+ self.__caller_type = caller_type
+
+ def __call__(self, env, *args, **kwds):
+ if not self.__contextual:
+ return self.__f(env, *args, **kwds)
+
+ caller = env.callframe_at(0)
+ f_name = self.__f.__name__
+
+ if not caller:
+ raise ConfigLoadException(
+ f"contextual function '{f_name}' can not be called contextless",
+ None
+ )
+
+ if self.__caller_type and not any([isinstance(caller, x) for x in self.__caller_type]):
+ raise ConfigLoadException(
+ f"caller of '{f_name}' ({caller}) must have type of any {self.__caller_type}",
+ caller
+ )
+
+ return self.__f(env, caller, *args, **kwds)
+
+def contextual(name=None, caller_type=[]):
+ def wrapper(func):
+ return LConfigBuiltin(func, True, name, caller_type)
+ return wrapper
+
+def builtin(name=None):
+ def wrapper(func):
+ return LConfigBuiltin(func, False, name)
+ return wrapper
--- /dev/null
+from .api import contextual, builtin
+from .lcnodes import LCFuncNode, LCTermNode, LCModuleNode
+from lib.utils import join_path
+import os
+
+@contextual()
+def v(env, caller, term):
+ node = env.lookup_node(term.__name__)
+ env.dependency().add(node, caller)
+
+ return env.lookup_value(node.get_name())
+
+@contextual(caller_type=[LCModuleNode])
+def include(env, caller, file):
+ fobj = caller.get_fo()
+ path = os.path.dirname(fobj.filename())
+
+ env.resolve_module(join_path(path, file))
+
+@contextual("type", caller_type=[LCTermNode])
+def term_type(env, caller, type):
+ caller.set_type(type)
+
+@contextual("add_to_collection", caller_type=[LCFuncNode])
+def parent(env, caller, ref):
+ sym = env.lookup_node(ref.__name__)
+
+ caller.set_parent(sym)
+
+@contextual(caller_type=[LCTermNode])
+def default(env, caller, val):
+ caller.set_default(val)
\ No newline at end of file
--- /dev/null
+import os.path as path
+import ast, json
+
+from .lcnodes import LCModuleNode
+from .api import (
+ ConfigLoadException,
+ Renderable
+)
+
+class LConfigFile:
+ def __init__(self,
+ env,
+ file) -> None:
+ self.__env = env
+ self.__file = env.to_wspath(file)
+
+ with open(file) as f:
+ tree = ast.parse(f.read(), self.__file, mode='exec')
+ self.__module = LCModuleNode(self, tree)
+
+ def filename(self):
+ return self.__file
+
+ def env(self):
+ return self.__env
+
+ def root_module(self):
+ return self.__module
+
+ def compile_astns(self, astns):
+ if not isinstance(astns, list):
+ astns = [astns]
+
+ return compile(
+ ast.Module(body=astns, type_ignores=[]),
+ self.__file, mode='exec')
+
+class DependencyGraph:
+ def __init__(self) -> None:
+ self._edges = {}
+
+ def add(self, dependent, dependee):
+ if dependent in self._edges:
+ if dependee in self._edges[dependent]:
+ return
+ self._edges[dependent].add(dependee)
+ else:
+ self._edges[dependent] = set([dependee])
+
+ if self.__check_loop(dependee):
+ raise ConfigLoadException(
+ f"loop dependency detected: {dependent.get_name()}",
+ dependee)
+
+ def __check_loop(self, start):
+ visited = set()
+ q = [start]
+ while len(q) > 0:
+ current = q.pop()
+ if current in visited:
+ return True
+
+ visited.add(current)
+ if current not in self._edges:
+ continue
+ for x in self._edges[current]:
+ q.append(x)
+
+ return False
+
+ def cascade(self, start):
+ q = [start]
+ while len(q) > 0:
+ current = q.pop()
+ if current in self._edges:
+ for x in self._edges[current]:
+ q.append(x)
+ current.evaluate()
+
+class ConfigTypeFactory:
+ def __init__(self) -> None:
+ self.__providers = []
+
+ def regitser(self, provider_type):
+ self.__providers.append(provider_type)
+
+ def create(self, typedef):
+ for provider in self.__providers:
+ if not provider.typedef_matched(typedef):
+ continue
+ return provider(typedef)
+
+ raise ConfigLoadException(
+ f"no type provider defined for type: {typedef}", None)
+
+class LConfigEvaluationWrapper:
+ def __init__(self, env, node) -> None:
+ self.__env = env
+ self.__node = node
+
+ def __enter__(self):
+ self.__env.push_executing_node(self.__node)
+ return self
+
+ def __exit__(self, type, value, tb):
+ self.__env.pop_executing_node()
+
+ def evaluate(self):
+ return self.__env.evaluate_node(self.__node)
+
+class LConfigEnvironment(Renderable):
+ def __init__(self, workspace, config_io) -> None:
+ super().__init__()
+
+ self.__ws_path = path.abspath(workspace)
+ self.__exec_globl = globals()
+ self.__eval_stack = []
+ self.__lc_modules = []
+ self.__config_val = {}
+ self.__node_table = {}
+ self.__deps_graph = DependencyGraph()
+ self.__type_fatry = ConfigTypeFactory()
+ self.__config_io = config_io
+
+ def to_wspath(self, _path):
+ _path = path.abspath(_path)
+ return path.relpath(_path, self.__ws_path)
+
+ def register_builtin_func(self, func_obj):
+ call = (lambda *arg, **kwargs:
+ func_obj(self, *arg, **kwargs))
+
+ self.__exec_globl[func_obj.name] = call
+
+ def resolve_module(self, file):
+ fo = LConfigFile(self, file)
+ self.__lc_modules.insert(0, (fo.root_module()))
+
+ def evaluate_node(self, node):
+ name = node.get_name()
+
+ return self.get_symbol(name)()
+
+ def eval_context(self, node):
+ return LConfigEvaluationWrapper(self, node)
+
+ def push_executing_node(self, node):
+ self.__eval_stack.append(node)
+
+ def pop_executing_node(self):
+ return self.__eval_stack.pop()
+
+ def register_node(self, node):
+ self.__node_table[node.get_name()] = node
+
+ l = {}
+ try:
+ self.push_executing_node(node)
+ exec(node.get_co(), self.__exec_globl, l)
+ self.pop_executing_node()
+ except Exception as e:
+ raise ConfigLoadException("failed to load", node, e)
+
+ self.__exec_globl.update(l)
+
+ def lookup_node(self, name):
+ if name not in self.__node_table:
+ raise ConfigLoadException(f"node '{name}' undefined")
+ return self.__node_table[name]
+
+ def type_factory(self):
+ return self.__type_fatry
+
+ def update_value(self, key, value):
+ self.__config_val[key] = value
+
+ def get_symbol(self, name):
+ if name not in self.__exec_globl:
+ raise ConfigLoadException(f"unknown symbol '{name}'")
+ return self.__exec_globl[name]
+
+ def callframe_at(self, traverse_level):
+ try:
+ return self.__eval_stack[traverse_level - 1]
+ except:
+ return None
+
+ def lookup_value(self, key):
+ return self.__config_val[key]
+
+ def dependency(self):
+ return self.__deps_graph
+
+ def update(self):
+ for mod in self.__lc_modules:
+ mod.evaluate()
+
+ def export(self):
+ self.__config_io.export(self, self.__config_val)
+
+ def save(self, _path = ".config.json"):
+ data = {}
+ for mod in self.__lc_modules:
+ mod.serialise(data)
+
+ with open(_path, 'w') as f:
+ json.dump(data, f)
+
+ def load(self, _path = ".config.json"):
+ if not path.exists(_path):
+ return
+
+ data = {}
+ with open(_path, 'r') as f:
+ data.update(json.load(f))
+
+ for mod in self.__lc_modules:
+ mod.deserialise(data)
+
+ self.update()
+
+ def render(self, rctx):
+ for mod in self.__lc_modules:
+ mod.render(rctx)
\ No newline at end of file
--- /dev/null
+from .api import (
+ ConfigLoadException,
+ ConfigTypeCheckError,
+ Renderable
+)
+
+from .utils import (
+ extract_decorators,
+ to_displayable
+)
+
+import ast, textwrap
+from abc import abstractmethod
+
+
+
+class LCNode(Renderable):
+ def __init__(self, fo, astn):
+ super().__init__()
+
+ self._fo = fo
+ self._env = fo.env()
+ self._astn = self.prune_astn(astn)
+ self._co = self.compile_astn()
+ self._parent = None
+
+ self._env.register_node(self)
+
+ def prune_astn(self, astn):
+ return astn
+
+ def compile_astn(self):
+ return self._fo.compile_astns(self._astn)
+
+ def get_co(self):
+ return self._co
+
+ def get_fo(self):
+ return self._fo
+
+ def set_parent(self, new_parent):
+ self._parent = new_parent
+
+ def parent(self):
+ return self._parent
+
+ def add_child(self, node):
+ pass
+
+ def remove_child(self, node):
+ pass
+
+ def get_name(self):
+ return f"LCNode: {self.__hash__()}"
+
+ @abstractmethod
+ def evaluate(self):
+ pass
+
+ def render(self, rctx):
+ pass
+
+ def deserialise(self, dict):
+ pass
+
+ def serialise(self, dict):
+ pass
+
+
+
+class LCModuleNode(LCNode):
+ def __init__(self, fo, astn: ast.Module):
+ self.__nodes = {}
+
+ super().__init__(fo, astn)
+
+ def get_name(self):
+ return f"file: {self._fo.filename()}"
+
+ def prune_astn(self, astn: ast.Module):
+ general_exprs = []
+
+ for b in astn.body:
+ if not isinstance(b, ast.FunctionDef):
+ general_exprs.append(b)
+ continue
+
+ node = LCFuncNode.construct(self._fo, b)
+ node.set_parent(self)
+
+ self.add_child(node)
+
+ return general_exprs
+
+ def evaluate(self):
+ with self._env.eval_context(self) as evalc:
+ ls = list(self.__nodes.values())
+ for node in ls:
+ node.evaluate()
+
+ def add_child(self, node):
+ self.__nodes[node] = node
+
+ def remove_child(self, node):
+ if node in self.__nodes:
+ del self.__nodes[node]
+
+ def deserialise(self, dict):
+ for node in self.__nodes:
+ node.deserialise(dict)
+
+ def serialise(self, dict):
+ for node in self.__nodes:
+ node.serialise(dict)
+
+ def render(self, rctx):
+ for node in self.__nodes:
+ node.render(rctx)
+
+class LCFuncNode(LCNode):
+ def __init__(self, fo, astn) -> None:
+ self._decors = {}
+ self._name = None
+ self._help = ""
+ self._display_name = None
+ self._enabled = True
+
+ super().__init__(fo, astn)
+
+ def prune_astn(self, astn: ast.FunctionDef):
+ self._name = astn.name
+ self._display_name = to_displayable(self._name)
+
+ maybe_doc = astn.body[0]
+ if isinstance(maybe_doc, ast.Expr):
+ if isinstance(maybe_doc.value, ast.Constant):
+ self._help = maybe_doc.value.value
+ self._help = textwrap.dedent(self._help)
+
+ decors = extract_decorators(astn)
+ for name, args, kwargs in decors:
+ self._decors[name] = (args, kwargs)
+
+ (args, _) = self._decors[self.mapped_name()]
+ if args:
+ self._display_name = args[0]
+
+ astn.decorator_list.clear()
+ return astn
+
+ def get_name(self):
+ return self._name
+
+ def get_display_name(self):
+ return self._display_name
+
+ def enabled(self):
+ if isinstance(self._parent, LCFuncNode):
+ return self._enabled and self._parent.enabled()
+ return self._enabled
+
+ def help_prompt(self):
+ return self._help
+
+ def evaluate(self):
+ with self._env.eval_context(self) as evalc:
+ result = evalc.evaluate()
+ self._enabled = True if result is None else result
+
+ @staticmethod
+ def mapped_name(self):
+ return None
+
+ @staticmethod
+ def construct(fo, astn: ast.FunctionDef):
+ nodes = [
+ LCCollectionNode,
+ LCGroupNode,
+ LCTermNode
+ ]
+
+ for node in nodes:
+ if extract_decorators(astn, node.mapped_name(), True):
+ return node(fo, astn)
+
+ raise ConfigLoadException(
+ f"unknown type for astn type: {type(astn)}")
+
+ def set_parent(self, new_parent):
+ if self._parent:
+ self._parent.remove_child(self)
+
+ new_parent.add_child(self)
+ super().set_parent(new_parent)
+
+
+class LCTermNode(LCFuncNode):
+ def __init__(self, fo, astn) -> None:
+ self._value = None
+ self._default = None
+ self._type = None
+ self._rdonly = False
+
+ super().__init__(fo, astn)
+
+ @staticmethod
+ def mapped_name():
+ return "Term"
+
+ def prune_astn(self, astn: ast.FunctionDef):
+ astn = super().prune_astn(astn)
+
+ self._rdonly = "ReadOnly" in self._decors
+
+ return astn
+
+ def set_type(self, type_def):
+ self._type = self._env.type_factory().create(type_def)
+
+ def get_type(self):
+ return self._type
+
+ def __assert_type(self, val):
+ if not self._type:
+ raise ConfigLoadException(
+ f"config: {self._name} must be typed", self)
+
+ if self._type.check(val):
+ return
+
+ raise ConfigTypeCheckError(
+ f"value: {val} does not match type {self._type}", self)
+
+ def set_value(self, val):
+ if self._rdonly:
+ return
+
+ if isinstance(val, str):
+ val = self._type.parse_input(val)
+
+ self.__assert_type(val)
+ self._value = val
+ self.__update_value()
+ self._env.dependency().cascade(self)
+
+ def set_default(self, val):
+ self.__assert_type(val)
+ self._default = val
+
+ if self._rdonly:
+ self._value = val
+
+ def get_value(self):
+ return self._value
+
+ def evaluate(self):
+ super().evaluate()
+ self.__update_value()
+
+ def read_only(self):
+ return self._rdonly
+
+ def render(self, rctx):
+ if self.enabled():
+ rctx.add_field(self._display_name, self)
+
+ def serialise(self, dict):
+ s_val = self._type.serialise(self._value)
+ dict[self._name] = s_val
+
+ def deserialise(self, dict):
+ if self._name not in dict:
+ return
+
+ s_val = dict[self._name]
+ v = self._type.deserialise(s_val)
+ self.__assert_type(v)
+ self._value = v
+
+
+ def __update_value(self):
+ v = self._value
+
+ if not self.enabled():
+ self._env.update_value(self._name, None)
+ return
+
+ if v is None:
+ v = self._default
+
+ if v is None and not self._type.allow_none():
+ raise ConfigLoadException(
+ f"config: {self._name} must have a value", self)
+
+ self._value = v
+ self._env.update_value(self._name, v)
+
+
+
+
+class LCGroupNode(LCFuncNode):
+ def __init__(self, fo, astn) -> None:
+ self._children = {}
+
+ super().__init__(fo, astn)
+
+ @staticmethod
+ def mapped_name():
+ return "Group"
+
+ def prune_astn(self, astn: ast.FunctionDef):
+ astn = super().prune_astn(astn)
+
+ other_exprs = []
+ for expr in astn.body:
+ if not isinstance(expr, ast.FunctionDef):
+ other_exprs.append(expr)
+ continue
+
+ node = LCFuncNode.construct(self._fo, expr)
+ node.set_parent(self)
+ self._children[node] = node
+
+ if not other_exprs:
+ other_exprs.append(
+ ast.Pass(
+ lineno=astn.lineno + 1,
+ col_offset=astn.col_offset
+ )
+ )
+
+ astn.body = other_exprs
+ return astn
+
+ def evaluate(self):
+ old_enable = super().enabled()
+ super().evaluate()
+
+ new_enabled = super().enabled()
+ if not new_enabled and old_enable == new_enabled:
+ return
+
+ with self._env.eval_context(self) as evalc:
+ children = list(self._children.values())
+ for child in children:
+ child.evaluate()
+
+ def render(self, rctx):
+ for child in self._children.values():
+ child.render(rctx)
+
+ def serialise(self, dict):
+ sub_dict = {}
+ for child in self._children.values():
+ child.serialise(sub_dict)
+
+ dict[self._name] = sub_dict
+
+ def deserialise(self, dict):
+ if self._name not in dict:
+ return
+
+ sub_dict = dict[self._name]
+ for child in self._children.values():
+ child.deserialise(sub_dict)
+
+ def add_child(self, node):
+ self._children[node] = node
+
+ def remove_child(self, node):
+ if node in self._children:
+ del self._children[node]
+
+
+class LCCollectionNode(LCGroupNode):
+ def __init__(self, fo, astn) -> None:
+ super().__init__(fo, astn)
+
+ @staticmethod
+ def mapped_name():
+ return "Collection"
+
+ def render(self, rctx):
+ _super = super()
+ rctx.add_expandable(
+ self._display_name,
+ self,
+ lambda _ctx:
+ _super.render(_ctx)
+ )
\ No newline at end of file
--- /dev/null
+from .api import TypeProviderBase
+from .utils import is_primitive, is_basic
+
+class PrimitiveType(TypeProviderBase):
+ def __init__(self, type) -> None:
+ super().__init__(type)
+
+ @staticmethod
+ def typedef_matched(typedef):
+ return is_primitive(typedef)
+
+ def check(self, val):
+ return isinstance(val, self._type)
+
+ def serialise(self, val):
+ return str(val)
+
+ def deserialise(self, val):
+ if val.lower() == "true":
+ return True
+ elif val.lower() == "false":
+ return False
+
+ return self._type(val)
+
+ def parse_input(self, input_str):
+ return self.deserialise(input_str)
+
+ def to_input(self, val):
+ return self.serialise(val)
+
+ def __str__(self) -> str:
+ if isinstance(self._type, type):
+ return f"any with type of {self._type}"
+ return f"exact of value '{self._type}'"
+
+
+class MultipleChoiceType(PrimitiveType):
+ def __init__(self, type) -> None:
+ super().__init__(type)
+
+ @staticmethod
+ def typedef_matched(typedef):
+ if not isinstance(typedef, list):
+ return False
+ return all([is_basic(x) for x in typedef])
+
+ def check(self, val):
+ if not is_basic(val):
+ return False
+ return val in self._type
+
+ def parse_input(self, input_str):
+ return super().parse_input(input_str)
+
+ def deserialise(self, val):
+ if val.lower() == "true":
+ return True
+ elif val.lower() == "false":
+ return False
+
+ for sv in self._type:
+ if val != str(sv):
+ continue
+ return type(sv)(val)
+
+ return None
+
+ def allow_none(self):
+ return None in self._type
+
+ def __str__(self) -> str:
+ accepted = [f" {t}" for t in self._type]
+ return "\n".join([
+ "choose one: \n",
+ *accepted
+ ])
--- /dev/null
+import ast
+
+def extract_decorators(fn_ast: ast.FunctionDef, fname = None, only_name=False):
+ decors = fn_ast.decorator_list
+ results = []
+ for decor in decors:
+ _args = []
+ _kwargs = []
+ if isinstance(decor, ast.Name):
+ name = decor.id
+ elif isinstance(decor, ast.Call):
+ if not isinstance(decor.func, ast.Name):
+ continue
+ name = decor.func.id
+ _args = decor.args
+ _kwargs = decor.keywords
+ else:
+ continue
+
+ if fname and name != fname:
+ continue
+
+ if only_name:
+ results.append(name)
+ continue
+
+ unpacked = []
+ kwargs = {}
+ for arg in _args:
+ if isinstance(arg, ast.Constant):
+ unpacked.append(arg.value)
+
+ for kwarg in _kwargs:
+ if isinstance(kwarg.value, ast.Constant):
+ kwargs[kwarg.arg] = kwarg.value.value
+
+ results.append((name, unpacked, kwargs))
+
+ return results
+
+def to_displayable(name):
+ l = name.strip().split('_')
+ for i, s in enumerate(l):
+ l[i] = str.upper(s[0]) + s[1:]
+ return " ".join(l)
+
+def is_primitive(val):
+ return val in [int, str, bool]
+
+def is_basic(val):
+ basic = [int, str, bool]
+ return (
+ val in basic or
+ any([isinstance(val, x) for x in basic])
+ )
\ No newline at end of file
--- /dev/null
+import traceback, sys
+
+class InterpreterException(Exception):
+ pass
+
+class Sandbox:
+ def __init__(self, symbols) -> None:
+ self.__syms = globals()
+ self.__syms.update(symbols)
+
+ def execute(self, file):
+ with open(file) as f:
+ return self.executes(f.read(), file)
+
+ def executes(self, str, file=""):
+ try:
+ local_ctx = {}
+ glb_ctx = self.__syms.copy()
+ exec(str, glb_ctx, local_ctx)
+ return local_ctx
+ except SyntaxError as err:
+ error_class = err.__class__.__name__
+ detail = err.args[0]
+ line_number = err.lineno
+ except Exception as err:
+ error_class = err.__class__.__name__
+ detail = err.args[0]
+ cl, exc, tb = sys.exc_info()
+ line_number = traceback.extract_tb(tb)[1][1]
+
+ print(f"LunaBuild failed: {error_class} at ./{file}:{line_number}, {detail}")
+ raise InterpreterException("load error")
\ No newline at end of file
--- /dev/null
+import os
+
+def join_path(stem, path):
+ if os.path.isabs(path):
+ return path
+ return os.path.join(stem, path)
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python
+
+from lbuild.contract import LunaBuildFile
+from lbuild.common import BuildEnvironment
+
+from lcfg.common import LConfigEnvironment
+from integration.config_io import CHeaderConfigProvider
+from integration.lbuild_bridge import LConfigProvider
+from integration.render_ishell import InteractiveShell
+
+import lcfg.types as lcfg_type
+import lcfg.builtins as builtin
+
+from os import getcwd
+from os import mkdir
+from os.path import abspath, basename, dirname, exists
+from argparse import ArgumentParser
+from lib.utils import join_path
+
+def prepare_lconfig_env(out_dir):
+ provider = CHeaderConfigProvider(join_path(out_dir, "configs.h"))
+ env = LConfigEnvironment(getcwd(), provider)
+
+ env.register_builtin_func(builtin.v)
+ env.register_builtin_func(builtin.term_type)
+ env.register_builtin_func(builtin.parent)
+ env.register_builtin_func(builtin.default)
+ env.register_builtin_func(builtin.include)
+
+ env.type_factory().regitser(lcfg_type.PrimitiveType)
+ env.type_factory().regitser(lcfg_type.MultipleChoiceType)
+
+ return env
+
+def do_config(lcfg_env):
+ shell = InteractiveShell(lcfg_env)
+ if not shell.render_loop():
+ print("Configuration aborted.")
+ exit(-1)
+
+ lcfg_env.export()
+ lcfg_env.save()
+
+def do_buildfile_gen(opts, lcfg_env):
+ root_path = abspath(opts.root)
+ ws_path = dirname(root_path)
+ root_name = basename(root_path)
+
+ env = BuildEnvironment(ws_path)
+
+ cfg_provider = LConfigProvider(lcfg_env)
+ env.set_config_provider(cfg_provider)
+
+ root = LunaBuildFile(env, root_name)
+
+ try:
+ root.resolve()
+ except Exception as err:
+ print("failed to resolve root build file")
+ raise err
+
+ env.export(opts.out_dir)
+
+def main():
+ parser = ArgumentParser()
+ parser.add_argument("--config", action="store_true", default=False)
+ parser.add_argument("--lconfig-file", default="LConfig")
+ parser.add_argument("root", nargs="?", default="LBuild")
+ parser.add_argument("-o", "--out-dir", required=True)
+
+ opts = parser.parse_args()
+ out_dir = opts.out_dir
+ if not exists(out_dir):
+ mkdir(out_dir)
+
+ lcfg_env = prepare_lconfig_env(out_dir)
+ lcfg_env.resolve_module(opts.lconfig_file)
+ lcfg_env.update()
+ lcfg_env.load()
+
+ if opts.config:
+ do_config(lcfg_env)
+ else:
+ do_buildfile_gen(opts, lcfg_env)
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
+++ /dev/null
-import jinja2
-import re
-import argparse
-import math
-import json
-from pathlib import Path
-from abc import ABC, abstractmethod
-
-class Preprocessor:
- reHex = re.compile(r"^0x([0-9a-fA-F]+)$")
- reGranuel = re.compile(r"^(?P<num>[0-9]+)@(?P<g>.+)$")
- reMacroRef = re.compile(r"^\*(?P<var>[a-zA-z0-9]+)$")
- reInt = re.compile(r"^[0-9]+$")
- def __init__(self) -> None:
- pass
-
- @staticmethod
- def expand_str(s: str, param_dict):
- if Preprocessor.reInt.match(s) is not None:
- return int(s)
-
- mo = Preprocessor.reHex.match(s)
- if mo is not None:
- return int(s, 16)
-
- mo = Preprocessor.reGranuel.match(s)
- if mo is not None:
- mg = mo.groupdict()
- num = int(mg['num'])
- granuel = param_dict["granule"][mg['g']]
- return num * granuel
-
- mo = Preprocessor.reMacroRef.match(s)
- if mo is not None:
- mg = mo.groupdict()
- return param_dict[mg['var']]
-
- return s.format_map(param_dict)
-
-class DataObject(ABC):
- def __init__(self, name, record):
- self.key = name
- self._record = record
- self.user_field = {}
- self.ctrl_field = {}
- self._parse(record)
-
- @staticmethod
- def create(record):
- return DataObject.create("", record)
-
- @staticmethod
- def create(key, record):
- if PrimitiveType.can_create(record):
- return PrimitiveType(record)
-
- name = key
- t = name if "$type" not in record else record['$type']
-
- if "$name" in record:
- name = record["$name"].strip()
-
- if not key.startswith('@') and "$type" not in record:
- return PlainOldObject(name, record)
-
- t = t.strip("@")
- if t == "list":
- return RangedObject(name, record)
- elif t == "foreach":
- return ForEachIndexObject(name, record)
- elif t == "case_range_index":
- return Condition(record)
- elif t == "data":
- return PlainOldObject(name, record)
- elif t == "define":
- return VariableDeclarationObject(record)
- elif t == "memory_map":
- return MemoryMapObject(record)
- else:
- return RawObject(name, record)
-
- def _parse(self, record):
- for k, v in record.items():
- if k.startswith("$"):
- self.ctrl_field[k.strip("$")] = FieldType.create(k, v)
- elif k.startswith("@"):
- self.ctrl_field[k.strip("@")] = DataObject.create(k, v)
- else:
- self.user_field[k] = DataObject.create(k, v)
-
- def expand(self, param={}):
- obj2 = {}
- for f in self.ctrl_field.values():
- if not isinstance(f, DataObject):
- continue
- obj2.update(**f.expand(param))
-
- obj = {}
- _param = {**param, **obj2}
- for k, v in self.user_field.items():
- if isinstance(v, DataObject):
- obj[k] = v.expand(_param)
- else:
- obj[k] = v
-
- return {**obj, **obj2}
-
-
-class FieldType:
- def __init__(self, record) -> None:
- self._record = record
- self._parse(record)
-
- @staticmethod
- def create(field, value):
- if field == "$range":
- return RangeType(value)
- else:
- return value
-
- @abstractmethod
- def _parse(self, record):
- pass
-
- @abstractmethod
- def get(self, param):
- pass
-
- def getraw(self):
- return self.__record
-
-class PrimitiveType(DataObject):
- def __init__(self, record) -> None:
- super().__init__("", record)
-
- @staticmethod
- def can_create(value):
- return type(value) in (str, int, bool)
-
- def _parse(self, record):
- if type(record) not in (str, int, bool):
- raise Exception(f"{type(self).__name__} require primitive type input")
- self.val = record
-
- if type(record) == str:
- self.__get_fn = self.__process_str
- else:
- self.__get_fn = lambda x: self.val
-
- def __process_str(self, param):
- return Preprocessor.expand_str(self.val, param)
-
- def expand(self, param={}):
- return self.__get_fn(param)
-
-class RangeType(FieldType):
- def __init__(self, record) -> None:
- self.__ranged_component = re.compile(r"^(?P<index>[^.]+)$|^(?P<start>.+?)\.\.(?P<end>.+)$")
- super().__init__(record)
-
- def _parse(self, record):
- return super()._parse(record)
-
- def get(self, param):
- record = self._record.strip('[]')
-
- self.__value=[]
- for component in record.split(','):
- component = component.strip()
- mo = self.__ranged_component.match(component)
- if mo is None:
- raise Exception(f"value '{component}' is not valid range component")
-
- mo = mo.groupdict()
- if mo["index"] is not None:
- self.__value.append(Preprocessor.expand_str(mo['index'], param))
- else:
- start = Preprocessor.expand_str(mo['start'], param)
- end = Preprocessor.expand_str(mo['end'], param)
- self.__value += [x for x in range(start, end + 1)]
- return self.__value
-
- def getraw(self):
- return self._record
-
-class VariableDeclarationObject(DataObject):
- def __init__(self, record):
- super().__init__("", record)
-
- def _parse(self, record):
- return super()._parse(record)
-
- def expand(self, param={}):
- obj = super().expand(param)
- param.update(**obj)
- return {}
-
-class Condition(DataObject):
- def __init__(self, record):
- super().__init__("", record)
-
- def _parse(self, record):
- super()._parse(record)
- if "range" not in self.ctrl_field:
- raise Exception("condition body must contains valid range case")
- if "true" not in self.ctrl_field:
- raise Exception("condition body must contains 'True' handling case")
-
-
- def expand(self, param={}):
- self.__range_lst = self.ctrl_field["range"].get(param)
- if param["index"] in self.__range_lst:
- return self.ctrl_field["true"].expand(param)
- elif "else" in self.ctrl_field:
- return self.ctrl_field["else"].expand(param)
- return {}
-
-class ArrayObject(DataObject):
- def __init__(self, record,
- nested_array = False,
- el_factory = lambda x: DataObject.create("", x)):
- self._el_factory = el_factory
- self._nested_array = nested_array
-
- super().__init__("", record)
-
- def _parse(self, record):
- if not isinstance(record, list):
- raise Exception(f"{type(self).__name__} require array input")
-
- self.content = []
- for x in record:
- self.content.append(self._el_factory(x))
-
- def expand(self, param={}):
- result = []
- for x in self.content:
- obj = x.expand(param)
- if isinstance(obj, list) and not self._nested_array:
- result += [*obj]
- else:
- result.append(obj)
-
- return result
-
-class MemoryMapObject(DataObject):
- class GranuleObject(DataObject):
- def __init__(self, record):
- super().__init__("", record)
-
- def _parse(self, record):
- self.__granules = {}
- for k, v in record.items():
- self.__granules[k] = DataObject.create(k, v)
-
- def expand(self, param={}):
- granules = {}
- for k, v in self.__granules.items():
- val = v.expand(param)
-
- if not isinstance(val, int):
- raise Exception("The granule definition must be either integer or int-castable string")
-
- granules[k] = val
-
- return {**granules}
-
- def __init__(self, record):
- super().__init__("", record)
-
- def _parse(self, record):
- for k, v in record.items():
- if k.startswith("$"):
- self.ctrl_field[k.strip("$")] = FieldType.create(k, v)
- elif k.startswith("@"):
- self.ctrl_field[k.strip("@")] = DataObject.create(k, v)
-
- if "granule" in record:
- self.__g = MemoryMapObject.GranuleObject(record["granule"])
-
- if "regions" in record:
- self.__regions = ArrayObject(record["regions"])
-
- if "width" in record:
- self.__width = DataObject.create("width", record["width"])
-
- def __process(self, start_addr, idx, regions):
- if idx >= len(regions):
- raise Exception("Unbounded region definition")
-
- e = regions[idx]
-
- if "start" not in e:
- ne = regions[idx + 1]
- if "start" not in ne or "size" not in e:
- e["start"] = start_addr
- else:
- self.__process(start_addr + e["size"], idx + 1, regions)
- e["start"] = ne['start'] - e["size"]
-
- if "block" in e:
- b = e["block"] - 1
- e["start"] = (e["start"] + b) & ~b
-
- if e["start"] < start_addr:
- raise Exception(f"starting addr {hex(e['start'])} overrlapping with {hex(start_addr)}")
-
- start_addr = e["start"]
-
- if "size" not in e:
- self.__process(start_addr, idx + 1, regions)
- ne = regions[idx + 1]
- e["size"] = ne['start'] - start_addr
-
- return start_addr + e["size"]
-
- def expand(self, param={}):
- super().expand(param)
-
- g = self.__g.expand(param)
-
- param["granule"] = g
-
- width = self.__width.expand(param)
- if not isinstance(width, int):
- raise Exception("'width' attribute must be integer")
-
- regions = self.__regions.expand(param)
-
- start_addr = 0
- for i in range(len(regions)):
- start_addr = self.__process(start_addr, i, regions)
-
- if math.log2(start_addr) > width:
- raise Exception("memory size larger than speicified address width")
-
- return {
- "granule": g,
- "regions": regions
- }
-
-class ForEachIndexObject(DataObject):
- def __init__(self, name, record):
- super().__init__(name, record)
- self.steps = []
- for k, v in record.items():
- self.steps.append(DataObject.create(k, v))
-
- def _parse(self, record):
- super()._parse(record)
-
- def expand(self, param={}):
- if "index" not in param:
- raise Exception(f"'{type(self).__name__}' require parameter 'index'.")
- obj = {}
- for cond in self.steps:
- obj.update(**cond.expand(param))
- return obj
-
-class RawObject(DataObject):
- def __init__(self, name, record):
- super().__init__(name, record)
-
- def _parse(self, record):
- return super()._parse(record)
-
- def expand(self, param={}):
- return super().expand(param)
-
-class PlainOldObject(DataObject):
- def __init__(self, name, record):
- super().__init__(name, record)
-
- def _parse(self, record):
- return super()._parse(record)
-
- def expand(self, param={}):
- return super().expand(param)
-
-class RangedObject(DataObject):
- def __init__(self, name, record):
- super().__init__(name, record)
-
- def _parse(self, record):
- super()._parse(record)
-
- def expand(self, param={}):
- if "range" not in self.ctrl_field:
- raise Exception("RangedObject with ranged type must have 'range' field defined")
-
- out_lst = []
- indices = self.ctrl_field["range"].get(param)
- for i in indices:
- param["index"] = i
- out_lst.append(super().expand(param))
-
- return out_lst
-
-def aligned(v, a):
- return v & ~(a - 1)
-
-class TemplateExpander:
- def __init__(self, template_path, project_path, arch) -> None:
- self.arch = arch
- self.tbase_path = template_path.joinpath(arch)
- self.pbase_path = project_path
-
- self.__helper_fns = {
- "align": aligned,
- "hex": lambda x: hex(x)
- }
-
- self.__load_config()
- self.__load_mappings()
-
- def __load_config(self):
- self.data = {}
- cfg_file: Path = self.tbase_path.joinpath("config.json")
- if not cfg_file.is_file():
- raise Exception(f"config not found. ({cfg_file})")
-
- obj = json.loads(cfg_file.read_text())
- for k, v in obj.items():
- o = DataObject.create(k, v).expand()
- self.data[k] = o
-
- def __load_mappings(self):
- self.mapping = {}
- mapping_file: Path = self.tbase_path.joinpath("mappings")
- if not mapping_file.is_file():
- raise Exception(f"config not found. ({mapping_file})")
-
- with mapping_file.open() as f:
- for l in f:
- l = l.strip()
-
- if not l:
- continue
-
- src, dest = l.split("->")
- src = src.strip()
-
- if src in self.mapping:
- raise Exception(f"repeating entry ({src})")
-
- self.mapping[src] = dest.strip()
-
- def render(self, selected = []):
- for k, v in self.mapping.items():
- src: Path = self.tbase_path.joinpath(k)
- dest: Path = self.pbase_path.joinpath(v)
- if (k not in selected):
- continue
-
- if not src.is_file():
- continue
-
- if not dest.parent.exists():
- dest.parent.mkdir(parents=True)
-
- self.data["template"] = k
- template = jinja2.Template(src.read_text(), trim_blocks=True)
- template.globals.update(**self.__helper_fns)
- out = template.render(data=self.data)
-
- dest.write_text(out)
-
- print(f"rendered: {k} -> {v}")
-
-import pprint
-
-def main():
- parser = argparse.ArgumentParser()
- parser.add_argument("selects", nargs="*")
- parser.add_argument("--arch", default='i386')
- parser.add_argument("-twd", "--template_dir", default=str(Path.cwd()))
- parser.add_argument("-pwd", "--project_dir", default=str(Path.cwd()))
-
- args = parser.parse_args()
-
- expander = TemplateExpander(Path(args.template_dir), Path(args.project_dir), args.arch)
-
- expander.render(args.selects)
- # pprint.pprint(expander.data)
-
-if __name__ == "__main__":
- main()
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+
+cat "${SCRIPT_DIR}/GRUB_TEMPLATE" | envsubst > "$1"
\ No newline at end of file
+++ /dev/null
-{
- "exception": {
- "@define": {
- "syscall_iv": 33,
- "iv_counts": 255
- },
- "ivdefs": {
- "$type": "list",
- "$range": "[0..*iv_counts]",
-
- "iv": "{index}",
-
- "@foreach": {
- "errcode": {
- "$type": "case_range_index",
- "$range": "[8,10..14,17]",
-
- "@true": {
- "has_errcode": true
- },
- "@else": {
- "has_errcode": false
- }
- },
- "dpl": {
- "$type": "case_range_index",
- "$range": "[*syscall_iv]",
-
- "@true": {
- "dpl": 3
- },
- "@else": {
- "dpl": 0
- }
- }
- }
- }
- },
- "sys_mmap": {
- "$type": "memory_map",
- "@define": {
- "page_mnts": 4,
- "vms_mnts": 1
- },
-
- "width": 32,
- "granule": {
- "page": "0x1000",
- "1M": "0x100000",
- "4M": "0x400000",
- "huge": "0x400000",
- "1G": "0x40000000"
- },
- "regions": [
- {
- "name": "kstack_area",
- "start": "1@1M",
- "size": "3@1M",
- "stk_align": 16
- },
- {
- "name": "usr_exec",
- "start": "4@1M",
- "size": "512@1M"
- },
- {
- "name": "usr_mmap"
- },
- {
- "name": "usr_stack",
- "size": "64@page",
- "stk_align": 16
- },
- {
- "name": "kernel_img",
- "start": "3@1G",
- "size": "16@4M",
- "block": "1@page"
- },
- {
- "$type": "list",
- "$range": "[1..*page_mnts]",
- "size": "1@page",
- "name": "pg_mount_{index}"
- },
- {
- "name": "pg_mount_var"
- },
- {
- "name": "vmap",
- "block": "1@huge"
- },
- {
- "$type": "list",
- "$range": "[1..*vms_mnts]",
- "name": "vms_mount_{index}",
- "size": "1@4M",
- "block": "1@huge"
- },
- {
- "name": "pd_ref",
- "start": "1023@4M",
- "size": "1@4M",
- "block": "1@huge"
- }
- ]
- }
-}
\ No newline at end of file
+++ /dev/null
-/* Generated from {{ data["template"] }}. Do NOT modify */
-
-#define __ASM__
-.macro isr_template vector, no_error_code=1
- .global _asm_isr\vector
- .type _asm_isr\vector, @function
- _asm_isr\vector:
- .if \no_error_code
- pushl $0x0
- .endif
- pushl $\vector
- jmp interrupt_wrapper
-.endm
-.section .text
-{% for isrdef in data["exception"]["ivdefs"] %}
-{% if isrdef["has_errcode"] %}
- isr_template {{ isrdef["iv"] }}, no_error_code=0
-{% else %}
- isr_template {{ isrdef["iv"] }}, no_error_code=1
-{% endif %}
-{% endfor %}
+++ /dev/null
-/* Generated from {{ data["template"] }}. Do NOT modify */
-
-#include <lunaix/types.h>
-#include <sys/i386_intr.h>
-
-#define IDT_INTERRUPT 0x70
-#define KERNEL_CS 0x8
-#define IDT_ATTR(dpl, type) (((type) << 5) | ((dpl & 3) << 13) | (1 << 15))
-#define IDT_ENTRY 256
-
-#define DECLARE_ISR(iv) extern void _asm_isr##iv();
-
-#define ISR_INSTALL(idt, iv, isr, dpl) \
- _idt[iv] = ((ptr_t)isr & 0xffff0000) | IDT_ATTR(dpl, IDT_INTERRUPT); \
- _idt[iv] <<= 32; \
- _idt[iv] |= (KERNEL_CS << 16) | ((ptr_t)isr & 0x0000ffff); \
-
-u64_t _idt[IDT_ENTRY];
-u16_t _idt_limit = sizeof(_idt) - 1;
-
-{% for isrdef in data["exception"]["ivdefs"] -%}
- DECLARE_ISR({{ isrdef["iv"] }})
-{% endfor %}
-
-void
-exception_install_handler()
-{
-{% for isrdef in data["exception"]["ivdefs"] %}
- ISR_INSTALL(_idt, {{ isrdef["iv"] }}, _asm_isr{{ isrdef["iv"] }}, {{ isrdef["dpl"] }})
-{% endfor %}
-}
\ No newline at end of file
+++ /dev/null
-i386_isrdef.c.j2 -> arch/i386/exceptions/i386_isrdef.c
-i386_intrhnds.S.j2 -> arch/i386/exceptions/intrhnds.S
-mempart.h.j2 -> arch/i386/includes/sys/mm/mempart.h
\ No newline at end of file
+++ /dev/null
-#ifndef __LUNAIX_MEMPART_H
-#define __LUNAIX_MEMPART_H
-/* Physical Adress Space Partition */
-/* Generated from {{ data["template"] }}. Do NOT modify */
-
-{% for k, v in data["sys_mmap"]["granule"].items() %}
-#define MEM_{{ k.upper() }} {{ hex(v) }}UL
-{% endfor %}
-
-{% for region in data["sys_mmap"]["regions"] %}
-#define {{ region["name"].upper() }} {{ hex(region["start"]) }}UL
-#define {{ region["name"].upper() }}_SIZE {{ hex(region["size"]) }}UL
-{% if "stk_align" in region %}
-#define {{ region["name"].upper() }}_END {{ hex(align(region["start"] + region["size"] - 1, region["stk_align"])) }}UL
-{% else %}
-#define {{ region["name"].upper() }}_END {{ hex((region["start"] + region["size"] - 1)) }}UL
-{% endif %}
-
-{% endfor %}
-#endif
\ No newline at end of file