From 1025235c72c31f7fa7b648c0e32ddcaa68a8f66a Mon Sep 17 00:00:00 2001 From: Lunaixsky Date: Thu, 27 Jun 2024 16:11:18 +0100 Subject: [PATCH 1/1] Introducing LunaBuild to the build flow (#36) * add LunaBuild for better control of source file selections * remove out-dated template based method * add LBuild files that follow LunaBuild methodology * integrate LunaBuild into the building flow * add documentation on general usage of LunaBuild * fix `make run` does not work properly * implement and integrate LConfig language * implement LConfig language for modularised kernel build experience * integrate LConfig into build flow * clean up out-dated files * Add interactive shell to do configuration * file clean up * General clean up * move grub image stuff to scripts/grub * remove the code format rule file - we don't need these fancy shit! * refine the config shell experience * allow accessing node using posix-path * able to identify whether the configuration is aborted or exit normally * fix up the dependencies in makefile, rebuild when needed * make sure the configuration shell only pop up when: 1) LConfigs changed 2) config.h disappeared * add proper implementation on header files change detection * add missing doc string on some config nodes --- lunaix-os/.clang-format | 142 ----- lunaix-os/.gitignore | 3 + lunaix-os/.prettierignore | 1 - lunaix-os/.vscode/c_cpp_properties.json | 3 +- lunaix-os/LBuild | 9 + lunaix-os/LConfig | 32 ++ lunaix-os/arch/LBuild | 7 + lunaix-os/arch/LConfig | 13 + lunaix-os/arch/i386/LBuild | 44 ++ lunaix-os/arch/i386/LConfig | 23 + lunaix-os/arch/i386/hal/LBuild | 9 + lunaix-os/arch/i386/klib/fast_crc.c | 2 +- lunaix-os/config-grub.sh | 3 - lunaix-os/config.h | 19 - lunaix-os/flags.h | 17 - lunaix-os/hal/LBuild | 9 + lunaix-os/hal/acpi/LBuild | 5 + lunaix-os/hal/ahci/LBuild | 9 + lunaix-os/hal/bus/LBuild | 3 + lunaix-os/hal/{ => bus}/pci.c | 0 lunaix-os/hal/char/LBuild | 8 + lunaix-os/hal/char/uart/LBuild | 4 + lunaix-os/hal/gfxa/LBuild | 5 + lunaix-os/hal/gfxa/vga/LBuild | 7 + lunaix-os/hal/rtc/LBuild | 3 + lunaix-os/hal/term/LBuild | 7 + lunaix-os/hal/timer/LBuild | 3 + lunaix-os/includes/lunaix/mm/pmm.h | 6 +- lunaix-os/includes/lunaix/spike.h | 4 +- lunaix-os/includes/sdbg/gdbstub.h | 1 + lunaix-os/kernel.mk | 36 +- lunaix-os/kernel/LBuild | 21 + lunaix-os/kernel/LConfig | 6 + lunaix-os/kernel/block/LBuild | 6 + lunaix-os/kernel/debug/LBuild | 5 + lunaix-os/kernel/debug/gdbstub.c | 1 - lunaix-os/kernel/device/LBuild | 8 + lunaix-os/kernel/ds/LBuild | 12 + lunaix-os/kernel/exe/LBuild | 5 + lunaix-os/kernel/exe/elf32/LBuild | 4 + lunaix-os/kernel/fs/LBuild | 16 + lunaix-os/kernel/fs/fs_export.c | 5 +- lunaix-os/kernel/fs/iso9660/LBuild | 8 + lunaix-os/kernel/fs/ramfs/LBuild | 3 + lunaix-os/kernel/fs/twifs/LBuild | 3 + lunaix-os/kernel/mm/LBuild | 16 + lunaix-os/kernel/mm/LConfig | 92 ++++ lunaix-os/kernel/mm/pmalloc_simple.c | 22 +- lunaix-os/kernel/process/LBuild | 9 + lunaix-os/ksrc.excludes | 2 - lunaix-os/libs/LBuild | 12 + lunaix-os/libs/klibc/string/strlen.c | 4 +- lunaix-os/makefile | 34 +- .../scripts/build-tools/README.lbuild.md | 101 ++++ .../build-tools/integration/__init__.py | 0 .../build-tools/integration/config_io.py | 52 ++ .../build-tools/integration/lbuild_bridge.py | 17 + .../build-tools/integration/render_ishell.py | 296 +++++++++++ .../scripts/build-tools/lbuild/__init__.py | 0 lunaix-os/scripts/build-tools/lbuild/api.py | 9 + .../scripts/build-tools/lbuild/common.py | 43 ++ .../scripts/build-tools/lbuild/contract.py | 101 ++++ .../scripts/build-tools/lcfg/__init__.py | 0 lunaix-os/scripts/build-tools/lcfg/api.py | 139 +++++ .../scripts/build-tools/lcfg/builtins.py | 32 ++ lunaix-os/scripts/build-tools/lcfg/common.py | 224 ++++++++ lunaix-os/scripts/build-tools/lcfg/lcnodes.py | 390 ++++++++++++++ lunaix-os/scripts/build-tools/lcfg/types.py | 77 +++ lunaix-os/scripts/build-tools/lcfg/utils.py | 55 ++ lunaix-os/scripts/build-tools/lib/__init__.py | 0 lunaix-os/scripts/build-tools/lib/sandbox.py | 32 ++ lunaix-os/scripts/build-tools/lib/utils.py | 6 + lunaix-os/scripts/build-tools/luna_build.py | 87 ++++ lunaix-os/scripts/expand.py | 487 ------------------ lunaix-os/{ => scripts/grub}/GRUB_TEMPLATE | 0 lunaix-os/scripts/grub/config-grub.sh | 5 + lunaix-os/scripts/templates/i386/config.json | 108 ---- .../scripts/templates/i386/i386_intrhnds.S.j2 | 21 - .../scripts/templates/i386/i386_isrdef.c.j2 | 31 -- lunaix-os/scripts/templates/i386/mappings | 3 - lunaix-os/scripts/templates/i386/mempart.h.j2 | 20 - 81 files changed, 2154 insertions(+), 913 deletions(-) delete mode 100644 lunaix-os/.clang-format delete mode 100644 lunaix-os/.prettierignore create mode 100644 lunaix-os/LBuild create mode 100644 lunaix-os/LConfig create mode 100644 lunaix-os/arch/LBuild create mode 100644 lunaix-os/arch/LConfig create mode 100644 lunaix-os/arch/i386/LBuild create mode 100644 lunaix-os/arch/i386/LConfig create mode 100644 lunaix-os/arch/i386/hal/LBuild delete mode 100755 lunaix-os/config-grub.sh delete mode 100644 lunaix-os/config.h delete mode 100644 lunaix-os/flags.h create mode 100644 lunaix-os/hal/LBuild create mode 100644 lunaix-os/hal/acpi/LBuild create mode 100644 lunaix-os/hal/ahci/LBuild create mode 100644 lunaix-os/hal/bus/LBuild rename lunaix-os/hal/{ => bus}/pci.c (100%) create mode 100644 lunaix-os/hal/char/LBuild create mode 100644 lunaix-os/hal/char/uart/LBuild create mode 100644 lunaix-os/hal/gfxa/LBuild create mode 100644 lunaix-os/hal/gfxa/vga/LBuild create mode 100644 lunaix-os/hal/rtc/LBuild create mode 100644 lunaix-os/hal/term/LBuild create mode 100644 lunaix-os/hal/timer/LBuild create mode 100644 lunaix-os/kernel/LBuild create mode 100644 lunaix-os/kernel/LConfig create mode 100644 lunaix-os/kernel/block/LBuild create mode 100644 lunaix-os/kernel/debug/LBuild create mode 100644 lunaix-os/kernel/device/LBuild create mode 100644 lunaix-os/kernel/ds/LBuild create mode 100644 lunaix-os/kernel/exe/LBuild create mode 100644 lunaix-os/kernel/exe/elf32/LBuild create mode 100644 lunaix-os/kernel/fs/LBuild create mode 100644 lunaix-os/kernel/fs/iso9660/LBuild create mode 100644 lunaix-os/kernel/fs/ramfs/LBuild create mode 100644 lunaix-os/kernel/fs/twifs/LBuild create mode 100644 lunaix-os/kernel/mm/LBuild create mode 100644 lunaix-os/kernel/mm/LConfig create mode 100644 lunaix-os/kernel/process/LBuild delete mode 100644 lunaix-os/ksrc.excludes create mode 100644 lunaix-os/libs/LBuild create mode 100644 lunaix-os/scripts/build-tools/README.lbuild.md create mode 100644 lunaix-os/scripts/build-tools/integration/__init__.py create mode 100644 lunaix-os/scripts/build-tools/integration/config_io.py create mode 100644 lunaix-os/scripts/build-tools/integration/lbuild_bridge.py create mode 100644 lunaix-os/scripts/build-tools/integration/render_ishell.py create mode 100644 lunaix-os/scripts/build-tools/lbuild/__init__.py create mode 100644 lunaix-os/scripts/build-tools/lbuild/api.py create mode 100644 lunaix-os/scripts/build-tools/lbuild/common.py create mode 100644 lunaix-os/scripts/build-tools/lbuild/contract.py create mode 100644 lunaix-os/scripts/build-tools/lcfg/__init__.py create mode 100644 lunaix-os/scripts/build-tools/lcfg/api.py create mode 100644 lunaix-os/scripts/build-tools/lcfg/builtins.py create mode 100644 lunaix-os/scripts/build-tools/lcfg/common.py create mode 100644 lunaix-os/scripts/build-tools/lcfg/lcnodes.py create mode 100644 lunaix-os/scripts/build-tools/lcfg/types.py create mode 100644 lunaix-os/scripts/build-tools/lcfg/utils.py create mode 100644 lunaix-os/scripts/build-tools/lib/__init__.py create mode 100644 lunaix-os/scripts/build-tools/lib/sandbox.py create mode 100644 lunaix-os/scripts/build-tools/lib/utils.py create mode 100755 lunaix-os/scripts/build-tools/luna_build.py delete mode 100644 lunaix-os/scripts/expand.py rename lunaix-os/{ => scripts/grub}/GRUB_TEMPLATE (100%) create mode 100755 lunaix-os/scripts/grub/config-grub.sh delete mode 100644 lunaix-os/scripts/templates/i386/config.json delete mode 100644 lunaix-os/scripts/templates/i386/i386_intrhnds.S.j2 delete mode 100644 lunaix-os/scripts/templates/i386/i386_isrdef.c.j2 delete mode 100644 lunaix-os/scripts/templates/i386/mappings delete mode 100644 lunaix-os/scripts/templates/i386/mempart.h.j2 diff --git a/lunaix-os/.clang-format b/lunaix-os/.clang-format deleted file mode 100644 index 240dd9a..0000000 --- a/lunaix-os/.clang-format +++ /dev/null @@ -1,142 +0,0 @@ -# # --- -# # 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 -# # --- - diff --git a/lunaix-os/.gitignore b/lunaix-os/.gitignore index 438c74f..eb55d3d 100644 --- a/lunaix-os/.gitignore +++ b/lunaix-os/.gitignore @@ -11,6 +11,9 @@ iso_inspect/ unused/ __pycache__/ +.builder/ +.config.json + .gdb_history **.o diff --git a/lunaix-os/.prettierignore b/lunaix-os/.prettierignore deleted file mode 100644 index b2eb281..0000000 --- a/lunaix-os/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -*.S.inc \ No newline at end of file diff --git a/lunaix-os/.vscode/c_cpp_properties.json b/lunaix-os/.vscode/c_cpp_properties.json index d3390d0..a0c612f 100644 --- a/lunaix-os/.vscode/c_cpp_properties.json +++ b/lunaix-os/.vscode/c_cpp_properties.json @@ -12,8 +12,7 @@ "-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", diff --git a/lunaix-os/LBuild b/lunaix-os/LBuild new file mode 100644 index 0000000..f4bb7c8 --- /dev/null +++ b/lunaix-os/LBuild @@ -0,0 +1,9 @@ +use("kernel") +use("libs") +use("arch") +use("hal") + +headers([ + "includes", + "includes/usr" +]) \ No newline at end of file diff --git a/lunaix-os/LConfig b/lunaix-os/LConfig new file mode 100644 index 0000000..3d0a179 --- /dev/null +++ b/lunaix-os/LConfig @@ -0,0 +1,32 @@ +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 diff --git a/lunaix-os/arch/LBuild b/lunaix-os/arch/LBuild new file mode 100644 index 0000000..8a4af8b --- /dev/null +++ b/lunaix-os/arch/LBuild @@ -0,0 +1,7 @@ +use({ + config("arch"): { + "i386": "i386", + "aarch64": "arm", + "rv64": "riscv" + } +}) \ No newline at end of file diff --git a/lunaix-os/arch/LConfig b/lunaix-os/arch/LConfig new file mode 100644 index 0000000..30c67b6 --- /dev/null +++ b/lunaix-os/arch/LConfig @@ -0,0 +1,13 @@ +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") diff --git a/lunaix-os/arch/i386/LBuild b/lunaix-os/arch/i386/LBuild new file mode 100644 index 0000000..d4c9a96 --- /dev/null +++ b/lunaix-os/arch/i386/LBuild @@ -0,0 +1,44 @@ +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 diff --git a/lunaix-os/arch/i386/LConfig b/lunaix-os/arch/i386/LConfig new file mode 100644 index 0000000..1445686 --- /dev/null +++ b/lunaix-os/arch/i386/LConfig @@ -0,0 +1,23 @@ + +@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 diff --git a/lunaix-os/arch/i386/hal/LBuild b/lunaix-os/arch/i386/hal/LBuild new file mode 100644 index 0000000..66fff6a --- /dev/null +++ b/lunaix-os/arch/i386/hal/LBuild @@ -0,0 +1,9 @@ +sources([ + "apic.c", + "rngx86.c", + "cpu.c", + "ps2kbd.c", + "apic_timer.c", + "ioapic.c", + "mc146818a.c" +]) \ No newline at end of file diff --git a/lunaix-os/arch/i386/klib/fast_crc.c b/lunaix-os/arch/i386/klib/fast_crc.c index 2384390..5332b76 100644 --- a/lunaix-os/arch/i386/klib/fast_crc.c +++ b/lunaix-os/arch/i386/klib/fast_crc.c @@ -1,7 +1,7 @@ #include #include -#ifdef CONFIG_X86_SSE4 +#ifdef CONFIG_X86_ENABLE_SSE_FEATURE unsigned int crc32b(unsigned char* data, unsigned int size) { diff --git a/lunaix-os/config-grub.sh b/lunaix-os/config-grub.sh deleted file mode 100755 index 956cb4f..0000000 --- a/lunaix-os/config-grub.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/bash - -cat GRUB_TEMPLATE | envsubst > "$1" \ No newline at end of file diff --git a/lunaix-os/config.h b/lunaix-os/config.h deleted file mode 100644 index 1d5386d..0000000 --- a/lunaix-os/config.h +++ /dev/null @@ -1,19 +0,0 @@ -#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 */ diff --git a/lunaix-os/flags.h b/lunaix-os/flags.h deleted file mode 100644 index 7f9835f..0000000 --- a/lunaix-os/flags.h +++ /dev/null @@ -1,17 +0,0 @@ -#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 */ diff --git a/lunaix-os/hal/LBuild b/lunaix-os/hal/LBuild new file mode 100644 index 0000000..c63faf3 --- /dev/null +++ b/lunaix-os/hal/LBuild @@ -0,0 +1,9 @@ +use("acpi") +use("ahci") +use("char") +use("gfxa") +use("rtc") +use("term") +use("timer") +use("bus") + diff --git a/lunaix-os/hal/acpi/LBuild b/lunaix-os/hal/acpi/LBuild new file mode 100644 index 0000000..fb5ee72 --- /dev/null +++ b/lunaix-os/hal/acpi/LBuild @@ -0,0 +1,5 @@ +sources([ + "parser/madt_parser.c", + "parser/mcfg_parser.c", + "acpi.c" +]) \ No newline at end of file diff --git a/lunaix-os/hal/ahci/LBuild b/lunaix-os/hal/ahci/LBuild new file mode 100644 index 0000000..8c081c6 --- /dev/null +++ b/lunaix-os/hal/ahci/LBuild @@ -0,0 +1,9 @@ +sources([ + "ahci_pci.c", + "hbadev_export.c", + "ahci.c", + "utils.c", + "io_event.c", + "atapi.c", + "ata.c" +]) \ No newline at end of file diff --git a/lunaix-os/hal/bus/LBuild b/lunaix-os/hal/bus/LBuild new file mode 100644 index 0000000..a485ce7 --- /dev/null +++ b/lunaix-os/hal/bus/LBuild @@ -0,0 +1,3 @@ +sources([ + "pci.c" +]) \ No newline at end of file diff --git a/lunaix-os/hal/pci.c b/lunaix-os/hal/bus/pci.c similarity index 100% rename from lunaix-os/hal/pci.c rename to lunaix-os/hal/bus/pci.c diff --git a/lunaix-os/hal/char/LBuild b/lunaix-os/hal/char/LBuild new file mode 100644 index 0000000..88b3310 --- /dev/null +++ b/lunaix-os/hal/char/LBuild @@ -0,0 +1,8 @@ +use("uart") + +sources([ + "devnull.c", + "serial.c", + "devzero.c", + "lxconsole.c", +]) \ No newline at end of file diff --git a/lunaix-os/hal/char/uart/LBuild b/lunaix-os/hal/char/uart/LBuild new file mode 100644 index 0000000..1fbc933 --- /dev/null +++ b/lunaix-os/hal/char/uart/LBuild @@ -0,0 +1,4 @@ +sources([ + "16550_base.c", + "16550_pmio.c" +]) \ No newline at end of file diff --git a/lunaix-os/hal/gfxa/LBuild b/lunaix-os/hal/gfxa/LBuild new file mode 100644 index 0000000..54d50d1 --- /dev/null +++ b/lunaix-os/hal/gfxa/LBuild @@ -0,0 +1,5 @@ +use("vga") + +sources([ + "gfxm.c" +]) \ No newline at end of file diff --git a/lunaix-os/hal/gfxa/vga/LBuild b/lunaix-os/hal/gfxa/vga/LBuild new file mode 100644 index 0000000..b05da8c --- /dev/null +++ b/lunaix-os/hal/gfxa/vga/LBuild @@ -0,0 +1,7 @@ +sources([ + "vga_pmio_ops.c", + "vga_gfxm_ops.c", + "vga.c", + "vga_mmio_ops.c", + "vga_pci.c" +]) \ No newline at end of file diff --git a/lunaix-os/hal/rtc/LBuild b/lunaix-os/hal/rtc/LBuild new file mode 100644 index 0000000..debfd14 --- /dev/null +++ b/lunaix-os/hal/rtc/LBuild @@ -0,0 +1,3 @@ +sources([ + "rtc_device.c" +]) \ No newline at end of file diff --git a/lunaix-os/hal/term/LBuild b/lunaix-os/hal/term/LBuild new file mode 100644 index 0000000..9325917 --- /dev/null +++ b/lunaix-os/hal/term/LBuild @@ -0,0 +1,7 @@ +sources([ + "term.c", + "console.c", + "term_io.c", + "lcntls/ansi_cntl.c", + "lcntls/lcntl.c", +]) \ No newline at end of file diff --git a/lunaix-os/hal/timer/LBuild b/lunaix-os/hal/timer/LBuild new file mode 100644 index 0000000..e5b2256 --- /dev/null +++ b/lunaix-os/hal/timer/LBuild @@ -0,0 +1,3 @@ +sources([ + "timer_device.c" +]) \ No newline at end of file diff --git a/lunaix-os/includes/lunaix/mm/pmm.h b/lunaix-os/includes/lunaix/mm/pmm.h index 60a5c68..b5dcb97 100644 --- a/lunaix-os/includes/lunaix/mm/pmm.h +++ b/lunaix-os/includes/lunaix/mm/pmm.h @@ -25,16 +25,16 @@ struct pmem_pool 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]; diff --git a/lunaix-os/includes/lunaix/spike.h b/lunaix-os/includes/lunaix/spike.h index cf40bab..e35cfdc 100644 --- a/lunaix-os/includes/lunaix/spike.h +++ b/lunaix-os/includes/lunaix/spike.h @@ -66,7 +66,7 @@ : 0) \ : (31 - clz(x))) -#ifndef __LUNAIXOS_NASSERT__ +#ifndef CONFIG_NO_ASSERT #define assert(cond) \ do { \ if (unlikely(!(cond))) { \ @@ -96,7 +96,7 @@ __assert_fail(const char* expr, const char* file, unsigned int line) #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); diff --git a/lunaix-os/includes/sdbg/gdbstub.h b/lunaix-os/includes/sdbg/gdbstub.h index f036d50..bd8b646 100644 --- a/lunaix-os/includes/sdbg/gdbstub.h +++ b/lunaix-os/includes/sdbg/gdbstub.h @@ -3,6 +3,7 @@ #include #include +#include struct gdb_state { diff --git a/lunaix-os/kernel.mk b/lunaix-os/kernel.mk index 189e970..9399e71 100644 --- a/lunaix-os/kernel.mk +++ b/lunaix-os/kernel.mk @@ -1,45 +1,33 @@ 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,$@) diff --git a/lunaix-os/kernel/LBuild b/lunaix-os/kernel/LBuild new file mode 100644 index 0000000..b52bc29 --- /dev/null +++ b/lunaix-os/kernel/LBuild @@ -0,0 +1,21 @@ +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 diff --git a/lunaix-os/kernel/LConfig b/lunaix-os/kernel/LConfig new file mode 100644 index 0000000..c820fd0 --- /dev/null +++ b/lunaix-os/kernel/LConfig @@ -0,0 +1,6 @@ +@Collection +def kernel_feature(): + """ Config kernel features """ + pass + +include("mm/LConfig") \ No newline at end of file diff --git a/lunaix-os/kernel/block/LBuild b/lunaix-os/kernel/block/LBuild new file mode 100644 index 0000000..252a19e --- /dev/null +++ b/lunaix-os/kernel/block/LBuild @@ -0,0 +1,6 @@ +sources([ + "blkpart_gpt.c", + "blk_mapping.c", + "blkio.c", + "block.c" +]) \ No newline at end of file diff --git a/lunaix-os/kernel/debug/LBuild b/lunaix-os/kernel/debug/LBuild new file mode 100644 index 0000000..d9ddb47 --- /dev/null +++ b/lunaix-os/kernel/debug/LBuild @@ -0,0 +1,5 @@ +sources([ + "failsafe.c", + "trace.c", + # "gdbstub.c" +]) \ No newline at end of file diff --git a/lunaix-os/kernel/debug/gdbstub.c b/lunaix-os/kernel/debug/gdbstub.c index c88ab8d..f84aab9 100644 --- a/lunaix-os/kernel/debug/gdbstub.c +++ b/lunaix-os/kernel/debug/gdbstub.c @@ -38,7 +38,6 @@ #include #include -#include /***************************************************************************** * Macros diff --git a/lunaix-os/kernel/device/LBuild b/lunaix-os/kernel/device/LBuild new file mode 100644 index 0000000..c3c7255 --- /dev/null +++ b/lunaix-os/kernel/device/LBuild @@ -0,0 +1,8 @@ +sources([ + "device.c", + "capability.c", + "devdb.c", + "devfs.c", + "input.c", + "poll.c" +]) diff --git a/lunaix-os/kernel/ds/LBuild b/lunaix-os/kernel/ds/LBuild new file mode 100644 index 0000000..e87f3d1 --- /dev/null +++ b/lunaix-os/kernel/ds/LBuild @@ -0,0 +1,12 @@ +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 diff --git a/lunaix-os/kernel/exe/LBuild b/lunaix-os/kernel/exe/LBuild new file mode 100644 index 0000000..8b24498 --- /dev/null +++ b/lunaix-os/kernel/exe/LBuild @@ -0,0 +1,5 @@ +use("elf32") + +sources([ + "exec.c" +]) \ No newline at end of file diff --git a/lunaix-os/kernel/exe/elf32/LBuild b/lunaix-os/kernel/exe/elf32/LBuild new file mode 100644 index 0000000..ef8cd25 --- /dev/null +++ b/lunaix-os/kernel/exe/elf32/LBuild @@ -0,0 +1,4 @@ +sources([ + "elf32bfmt.c", + "ldelf32.c" +]) \ No newline at end of file diff --git a/lunaix-os/kernel/fs/LBuild b/lunaix-os/kernel/fs/LBuild new file mode 100644 index 0000000..25e8446 --- /dev/null +++ b/lunaix-os/kernel/fs/LBuild @@ -0,0 +1,16 @@ +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 diff --git a/lunaix-os/kernel/fs/fs_export.c b/lunaix-os/kernel/fs/fs_export.c index bdc02d4..dffff0a 100644 --- a/lunaix-os/kernel/fs/fs_export.c +++ b/lunaix-os/kernel/fs/fs_export.c @@ -41,9 +41,8 @@ void __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__); diff --git a/lunaix-os/kernel/fs/iso9660/LBuild b/lunaix-os/kernel/fs/iso9660/LBuild new file mode 100644 index 0000000..aaa5edc --- /dev/null +++ b/lunaix-os/kernel/fs/iso9660/LBuild @@ -0,0 +1,8 @@ +sources([ + "inode.c", + "file.c", + "mount.c", + "directory.c", + "utils.c", + "rockridge.c" +]) \ No newline at end of file diff --git a/lunaix-os/kernel/fs/ramfs/LBuild b/lunaix-os/kernel/fs/ramfs/LBuild new file mode 100644 index 0000000..fbc9ec0 --- /dev/null +++ b/lunaix-os/kernel/fs/ramfs/LBuild @@ -0,0 +1,3 @@ +sources([ + "ramfs.c" +]) \ No newline at end of file diff --git a/lunaix-os/kernel/fs/twifs/LBuild b/lunaix-os/kernel/fs/twifs/LBuild new file mode 100644 index 0000000..b4270ce --- /dev/null +++ b/lunaix-os/kernel/fs/twifs/LBuild @@ -0,0 +1,3 @@ +sources([ + "twifs.c" +]) \ No newline at end of file diff --git a/lunaix-os/kernel/mm/LBuild b/lunaix-os/kernel/mm/LBuild new file mode 100644 index 0000000..e350f50 --- /dev/null +++ b/lunaix-os/kernel/mm/LBuild @@ -0,0 +1,16 @@ +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 diff --git a/lunaix-os/kernel/mm/LConfig b/lunaix-os/kernel/mm/LConfig new file mode 100644 index 0000000..7db89f5 --- /dev/null +++ b/lunaix-os/kernel/mm/LConfig @@ -0,0 +1,92 @@ + +@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 diff --git a/lunaix-os/kernel/mm/pmalloc_simple.c b/lunaix-os/kernel/mm/pmalloc_simple.c index 37a720b..230ae10 100644 --- a/lunaix-os/kernel/mm/pmalloc_simple.c +++ b/lunaix-os/kernel/mm/pmalloc_simple.c @@ -1,23 +1,23 @@ #include #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 diff --git a/lunaix-os/kernel/process/LBuild b/lunaix-os/kernel/process/LBuild new file mode 100644 index 0000000..3bb42be --- /dev/null +++ b/lunaix-os/kernel/process/LBuild @@ -0,0 +1,9 @@ +sources([ + "signal.c", + "sched.c", + "fork.c", + "process.c", + "taskfs.c", + "task_attr.c", + "thread.c" +]) \ No newline at end of file diff --git a/lunaix-os/ksrc.excludes b/lunaix-os/ksrc.excludes deleted file mode 100644 index ca3e1a5..0000000 --- a/lunaix-os/ksrc.excludes +++ /dev/null @@ -1,2 +0,0 @@ -kernel/debug/sdbg.c -kernel/debug/gdbstub.c \ No newline at end of file diff --git a/lunaix-os/libs/LBuild b/lunaix-os/libs/LBuild new file mode 100644 index 0000000..2f647b4 --- /dev/null +++ b/lunaix-os/libs/LBuild @@ -0,0 +1,12 @@ +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 diff --git a/lunaix-os/libs/klibc/string/strlen.c b/lunaix-os/libs/klibc/string/strlen.c index a1aec5a..5e09423 100644 --- a/lunaix-os/libs/klibc/string/strlen.c +++ b/lunaix-os/libs/klibc/string/strlen.c @@ -2,7 +2,7 @@ #include unsigned long weak -strlen(const char* str) +strlen(const char *str) { unsigned long len = 0; while (str[len]) @@ -11,7 +11,7 @@ strlen(const char* str) } 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) diff --git a/lunaix-os/makefile b/lunaix-os/makefile index 6e9fdb0..4e13211 100644 --- a/lunaix-os/makefile +++ b/lunaix-os/makefile @@ -28,9 +28,13 @@ $(DEPS): 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) @@ -39,10 +43,18 @@ $(kbuild_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 @@ -51,14 +63,15 @@ export KCMD=$(CMDLINE) 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) @@ -80,12 +93,13 @@ instable: all 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) diff --git a/lunaix-os/scripts/build-tools/README.lbuild.md b/lunaix-os/scripts/build-tools/README.lbuild.md new file mode 100644 index 0000000..8a25a8e --- /dev/null +++ b/lunaix-os/scripts/build-tools/README.lbuild.md @@ -0,0 +1,101 @@ +# 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 -o `. It will output two file +to the ``: `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. diff --git a/lunaix-os/scripts/build-tools/integration/__init__.py b/lunaix-os/scripts/build-tools/integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lunaix-os/scripts/build-tools/integration/config_io.py b/lunaix-os/scripts/build-tools/integration/config_io.py new file mode 100644 index 0000000..33079ff --- /dev/null +++ b/lunaix-os/scripts/build-tools/integration/config_io.py @@ -0,0 +1,52 @@ +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") diff --git a/lunaix-os/scripts/build-tools/integration/lbuild_bridge.py b/lunaix-os/scripts/build-tools/integration/lbuild_bridge.py new file mode 100644 index 0000000..7566670 --- /dev/null +++ b/lunaix-os/scripts/build-tools/integration/lbuild_bridge.py @@ -0,0 +1,17 @@ +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 diff --git a/lunaix-os/scripts/build-tools/integration/render_ishell.py b/lunaix-os/scripts/build-tools/integration/render_ishell.py new file mode 100644 index 0000000..5eacf55 --- /dev/null +++ b/lunaix-os/scripts/build-tools/integration/render_ishell.py @@ -0,0 +1,296 @@ +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 diff --git a/lunaix-os/scripts/build-tools/lbuild/__init__.py b/lunaix-os/scripts/build-tools/lbuild/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lunaix-os/scripts/build-tools/lbuild/api.py b/lunaix-os/scripts/build-tools/lbuild/api.py new file mode 100644 index 0000000..2bc91fa --- /dev/null +++ b/lunaix-os/scripts/build-tools/lbuild/api.py @@ -0,0 +1,9 @@ +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 diff --git a/lunaix-os/scripts/build-tools/lbuild/common.py b/lunaix-os/scripts/build-tools/lbuild/common.py new file mode 100644 index 0000000..c559b0a --- /dev/null +++ b/lunaix-os/scripts/build-tools/lbuild/common.py @@ -0,0 +1,43 @@ +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 diff --git a/lunaix-os/scripts/build-tools/lbuild/contract.py b/lunaix-os/scripts/build-tools/lbuild/contract.py new file mode 100644 index 0000000..1b818ab --- /dev/null +++ b/lunaix-os/scripts/build-tools/lbuild/contract.py @@ -0,0 +1,101 @@ +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 diff --git a/lunaix-os/scripts/build-tools/lcfg/__init__.py b/lunaix-os/scripts/build-tools/lcfg/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lunaix-os/scripts/build-tools/lcfg/api.py b/lunaix-os/scripts/build-tools/lcfg/api.py new file mode 100644 index 0000000..1b0b30f --- /dev/null +++ b/lunaix-os/scripts/build-tools/lcfg/api.py @@ -0,0 +1,139 @@ +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 diff --git a/lunaix-os/scripts/build-tools/lcfg/builtins.py b/lunaix-os/scripts/build-tools/lcfg/builtins.py new file mode 100644 index 0000000..9eb616f --- /dev/null +++ b/lunaix-os/scripts/build-tools/lcfg/builtins.py @@ -0,0 +1,32 @@ +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 diff --git a/lunaix-os/scripts/build-tools/lcfg/common.py b/lunaix-os/scripts/build-tools/lcfg/common.py new file mode 100644 index 0000000..b409f1d --- /dev/null +++ b/lunaix-os/scripts/build-tools/lcfg/common.py @@ -0,0 +1,224 @@ +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 diff --git a/lunaix-os/scripts/build-tools/lcfg/lcnodes.py b/lunaix-os/scripts/build-tools/lcfg/lcnodes.py new file mode 100644 index 0000000..cf869b0 --- /dev/null +++ b/lunaix-os/scripts/build-tools/lcfg/lcnodes.py @@ -0,0 +1,390 @@ +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 diff --git a/lunaix-os/scripts/build-tools/lcfg/types.py b/lunaix-os/scripts/build-tools/lcfg/types.py new file mode 100644 index 0000000..123f6a9 --- /dev/null +++ b/lunaix-os/scripts/build-tools/lcfg/types.py @@ -0,0 +1,77 @@ +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 + ]) diff --git a/lunaix-os/scripts/build-tools/lcfg/utils.py b/lunaix-os/scripts/build-tools/lcfg/utils.py new file mode 100644 index 0000000..96e9e2b --- /dev/null +++ b/lunaix-os/scripts/build-tools/lcfg/utils.py @@ -0,0 +1,55 @@ +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 diff --git a/lunaix-os/scripts/build-tools/lib/__init__.py b/lunaix-os/scripts/build-tools/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lunaix-os/scripts/build-tools/lib/sandbox.py b/lunaix-os/scripts/build-tools/lib/sandbox.py new file mode 100644 index 0000000..de28aca --- /dev/null +++ b/lunaix-os/scripts/build-tools/lib/sandbox.py @@ -0,0 +1,32 @@ +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 diff --git a/lunaix-os/scripts/build-tools/lib/utils.py b/lunaix-os/scripts/build-tools/lib/utils.py new file mode 100644 index 0000000..361b786 --- /dev/null +++ b/lunaix-os/scripts/build-tools/lib/utils.py @@ -0,0 +1,6 @@ +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 diff --git a/lunaix-os/scripts/build-tools/luna_build.py b/lunaix-os/scripts/build-tools/luna_build.py new file mode 100755 index 0000000..848ceb6 --- /dev/null +++ b/lunaix-os/scripts/build-tools/luna_build.py @@ -0,0 +1,87 @@ +#!/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 diff --git a/lunaix-os/scripts/expand.py b/lunaix-os/scripts/expand.py deleted file mode 100644 index 31e06da..0000000 --- a/lunaix-os/scripts/expand.py +++ /dev/null @@ -1,487 +0,0 @@ -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[0-9]+)@(?P.+)$") - reMacroRef = re.compile(r"^\*(?P[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[^.]+)$|^(?P.+?)\.\.(?P.+)$") - 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 diff --git a/lunaix-os/GRUB_TEMPLATE b/lunaix-os/scripts/grub/GRUB_TEMPLATE similarity index 100% rename from lunaix-os/GRUB_TEMPLATE rename to lunaix-os/scripts/grub/GRUB_TEMPLATE diff --git a/lunaix-os/scripts/grub/config-grub.sh b/lunaix-os/scripts/grub/config-grub.sh new file mode 100755 index 0000000..311550b --- /dev/null +++ b/lunaix-os/scripts/grub/config-grub.sh @@ -0,0 +1,5 @@ +#!/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 diff --git a/lunaix-os/scripts/templates/i386/config.json b/lunaix-os/scripts/templates/i386/config.json deleted file mode 100644 index b74d558..0000000 --- a/lunaix-os/scripts/templates/i386/config.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "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 diff --git a/lunaix-os/scripts/templates/i386/i386_intrhnds.S.j2 b/lunaix-os/scripts/templates/i386/i386_intrhnds.S.j2 deleted file mode 100644 index 26067ac..0000000 --- a/lunaix-os/scripts/templates/i386/i386_intrhnds.S.j2 +++ /dev/null @@ -1,21 +0,0 @@ -/* 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 %} diff --git a/lunaix-os/scripts/templates/i386/i386_isrdef.c.j2 b/lunaix-os/scripts/templates/i386/i386_isrdef.c.j2 deleted file mode 100644 index beec564..0000000 --- a/lunaix-os/scripts/templates/i386/i386_isrdef.c.j2 +++ /dev/null @@ -1,31 +0,0 @@ -/* Generated from {{ data["template"] }}. Do NOT modify */ - -#include -#include - -#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 diff --git a/lunaix-os/scripts/templates/i386/mappings b/lunaix-os/scripts/templates/i386/mappings deleted file mode 100644 index 04d7560..0000000 --- a/lunaix-os/scripts/templates/i386/mappings +++ /dev/null @@ -1,3 +0,0 @@ -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 diff --git a/lunaix-os/scripts/templates/i386/mempart.h.j2 b/lunaix-os/scripts/templates/i386/mempart.h.j2 deleted file mode 100644 index 01af898..0000000 --- a/lunaix-os/scripts/templates/i386/mempart.h.j2 +++ /dev/null @@ -1,20 +0,0 @@ -#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 -- 2.27.0