From c86df500638b10ca9987eed3e2eb8977b54c5609 Mon Sep 17 00:00:00 2001 From: jingus Date: Mon, 15 Jun 2026 21:53:39 -0500 Subject: [PATCH] feat: basic bump --- .envrc | 1 + .gitignore | 2 + Makefile | 11 + flake.lock | 77 ++++++ flake.nix | 11 + .../packages/boilerplate/boilerplate.py | 245 ++++++++++++++++++ nix-modules/packages/boilerplate/default.nix | 32 +++ nix-modules/parts.nix | 10 + nix-modules/shell.nix | 23 ++ src/jalloc.c | 14 + src/jalloc.h | 3 + src/main.c | 106 ++++++++ 12 files changed, 535 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 nix-modules/packages/boilerplate/boilerplate.py create mode 100644 nix-modules/packages/boilerplate/default.nix create mode 100644 nix-modules/parts.nix create mode 100644 nix-modules/shell.nix create mode 100644 src/jalloc.c create mode 100644 src/jalloc.h create mode 100644 src/main.c diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0834fff --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.build +.direnv diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1cd4a21 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +OUT_DIR = .build +OBJECTS = src/*.c + +prep-out: + @mkdir -p $(OUT_DIR)/bin + +build: prep-out + @gcc -o $(OUT_DIR)/bin/out $(OBJECTS) + +run: build + @$(OUT_DIR)/bin/out diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..51730b6 --- /dev/null +++ b/flake.lock @@ -0,0 +1,77 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1778716662, + "narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "import-tree": { + "locked": { + "lastModified": 1778781969, + "narHash": "sha256-Jjuz5CmSkur8KvLDoGa+vylEp+RkQtv4mt/qcMznpH0=", + "owner": "vic", + "repo": "import-tree", + "rev": "d321337efd0f23a9eb14a42adb7b2c29313ab274", + "type": "github" + }, + "original": { + "owner": "vic", + "repo": "import-tree", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1781074563, + "narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1777168982, + "narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "import-tree": "import-tree", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..3d702f9 --- /dev/null +++ b/flake.nix @@ -0,0 +1,11 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + + flake-parts.url = "github:hercules-ci/flake-parts"; + import-tree.url = "github:vic/import-tree"; + }; + + outputs = + inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./nix-modules); +} diff --git a/nix-modules/packages/boilerplate/boilerplate.py b/nix-modules/packages/boilerplate/boilerplate.py new file mode 100644 index 0000000..a6b498b --- /dev/null +++ b/nix-modules/packages/boilerplate/boilerplate.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import argparse +import json +import re +import subprocess +import sys +import textwrap +from pathlib import Path + + +KIND_LAYOUTS = { + "layer": Path("nix-modules/features/layers"), + "feature": Path("nix-modules/features"), + "host": Path("nix-modules/hosts"), + "package": Path("nix-modules/packages"), +} + + +NAME_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]*$") + + +def nix_string(value: str) -> str: + return json.dumps(value) + + +def git_root() -> Path | None: + result = subprocess.run( + ["git", "rev-parse", "--show-toplevel"], + check=False, + capture_output=True, + text=True, + ) + if result.returncode != 0: + return None + return Path(result.stdout.strip()) + + +def host_templates(name: str) -> dict[Path, str]: + return { + Path("default.nix"): + textwrap.dedent( + f"""\ + {{ self, inputs, ... }}: + {{ + flake.nixosConfigurations.{name} = inputs.nixpkgs.lib.nixosSystem {{ + system = builtins.currentSystem; + modules = [ + self.nixosModules.{name}Configuration + ]; + }}; + }} + """ + ), + Path("configuration.nix"): + textwrap.dedent( + f"""\ + {{ self, inputs, ... }}: + {{ + flake.nixosModules.{name}Configuration = + {{ pkgs, lib, ... }}: + {{ + imports = [ + self.nixosModules.{name}Hardware + ]; + system.stateVersion = "25.11"; + }}; + }} + """ + ), + Path("hardware-configuration.nix"): + textwrap.dedent( + f"""\ + {{ self, inputs, ... }}: + {{ + flake.nixosModules.{name}Hardware = + {{ lib, pkgs, ... }}: + {{ + config = {{ + # If you're building a full system here, you'd likely want to just copy the hardware + # configuration generated by the NixOS installer. I've added some stuff here just so + # `nix flake check` passes on initialization of the flake and to show how it _could_ + # look. You can find it here: `/etc/nixos/hardware-configuration.nix` + + boot.loader.grub.devices = [ "/dev/sda" ]; + + fileSystems."/" = {{ + device = "tmpfs"; + fsType = "tmpfs"; + }}; + }}; + }}; + }} + """ + ), + } + + +def template(kind: str, name: str) -> str: + if kind in {"layer", "feature"}: + return textwrap.dedent( + f"""\ + {{ self, inputs, ... }}: + {{ + flake.nixosModules.{name} = {{ pkgs, lib, ... }}: + {{ + config = {{ + }}; + }}; + }} + """ + ) + + if kind == "host": + quoted_name = nix_string(name) + return textwrap.dedent( + f"""\ + {{ self, inputs, ... }}: + {{ + flake.nixosModules.{name} = {{ pkgs, lib, ... }}: + {{ + config = {{ + networking.hostName = {quoted_name}; + # system.stateVersion = "25.11"; + }}; + }}; + + flake.nixosConfigurations.{name} = inputs.nixpkgs.lib.nixosSystem {{ + modules = [ + self.nixosModules.{name} + ]; + }}; + }} + """ + ) + + if kind == "package": + quoted_name = nix_string(name) + return textwrap.dedent( + f"""\ + {{ self, inputs, ... }}: + {{ + perSystem = + {{ pkgs, ... }}: + {{ + packages.{name} = pkgs.writeShellApplication {{ + name = {quoted_name}; + runtimeInputs = [ ]; + text = '' + echo "TODO: implement {name}" + ''; + }}; + }}; + }} + """ + ) + + raise ValueError(f"Unsupported kind: {kind}") + + +def planned_files(kind: str, name: str, multifile: bool) -> dict[Path, str]: + if kind == "host": + return host_templates(name) + + target = Path("default.nix") if multifile else Path(f"{name}.nix") + return {target: template(kind, name)} + + +def write_file(path: Path, content: str) -> None: + if path.exists(): + raise FileExistsError(f"Refusing to overwrite existing path: {path}") + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content.rstrip() + "\n", encoding="utf-8") + + +def stage(paths: list[Path], root: Path) -> None: + subprocess.run( + ["git", "add", "--", *[str(path.relative_to(root)) for path in paths]], + check=True, + cwd=root, + ) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Generate Nix boilerplate modules") + parser.add_argument("kind", choices=sorted(KIND_LAYOUTS)) + parser.add_argument("name") + parser.add_argument("--no-git-stage", action="store_true", help="Do not stage created files") + parser.add_argument("--dry-run", action="store_true", help="Print planned files without creating them") + parser.add_argument( + "--multifile", + action="store_true", + help="Create /default.nix instead of .nix", + ) + return parser.parse_args() + + +def main() -> int: + args = parse_args() + + if not NAME_RE.fullmatch(args.name): + print( + "name must match ^[A-Za-z0-9][A-Za-z0-9_.-]*$", + file=sys.stderr, + ) + return 2 + + root = git_root() + if root is None: + if args.dry_run or args.no_git_stage: + root = Path.cwd() + else: + print("not inside a git repository; use --no-git-stage", file=sys.stderr) + return 2 + + files = planned_files(args.kind, args.name, args.multifile) + base = root / KIND_LAYOUTS[args.kind] + target_base = base / args.name if args.kind == "host" else base + targets = [target_base / rel for rel in files] + + if args.dry_run: + print("dry run: no files will be created") + for target in targets: + print(target.relative_to(root)) + return 0 + + try: + for rel_path, content in files.items(): + write_file(target_base / rel_path, content) + except FileExistsError as err: + print(err, file=sys.stderr) + return 1 + + if not args.no_git_stage: + stage(targets, root) + + for target in targets: + print(target.relative_to(root)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/nix-modules/packages/boilerplate/default.nix b/nix-modules/packages/boilerplate/default.nix new file mode 100644 index 0000000..9498389 --- /dev/null +++ b/nix-modules/packages/boilerplate/default.nix @@ -0,0 +1,32 @@ +{ self, inputs, ... }: +{ + perSystem = + { pkgs, lib, ... }: + { + packages.boilerplate = pkgs.stdenvNoCC.mkDerivation { + pname = "boilerplate"; + version = "0.1.0"; + + src = ./.; + + dontConfigure = true; + dontBuild = true; + + installPhase = '' + runHook preInstall + + substituteInPlace boilerplate.py \ + --replace-fail '#!/usr/bin/env python3' '#!${pkgs.python3}/bin/python3' + install -Dm755 boilerplate.py "$out/bin/boilerplate" + + runHook postInstall + ''; + + meta = { + description = "Generate boilerplate Nix modules"; + license = lib.licenses.mit; + mainProgram = "boilerplate"; + }; + }; + }; +} diff --git a/nix-modules/parts.nix b/nix-modules/parts.nix new file mode 100644 index 0000000..657e3ca --- /dev/null +++ b/nix-modules/parts.nix @@ -0,0 +1,10 @@ +{ + config = { + systems = [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-linux" + "aarch64-darwin" + ]; + }; +} diff --git a/nix-modules/shell.nix b/nix-modules/shell.nix new file mode 100644 index 0000000..64ad929 --- /dev/null +++ b/nix-modules/shell.nix @@ -0,0 +1,23 @@ +{ ... }: +{ + perSystem = + { pkgs, self', ... }: + { + # An example devshell with some Rust and Nix tools + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + glibc + gcc + gnumake + clang + clang-tools + nixfmt + nil + alejandra + self'.packages.boilerplate + ]; + nativeBuildInputs = [ pkgs.pkg-config ]; + env.CPATH = "${pkgs.glibc.dev}/include"; + }; + }; +} diff --git a/src/jalloc.c b/src/jalloc.c new file mode 100644 index 0000000..7508b0e --- /dev/null +++ b/src/jalloc.c @@ -0,0 +1,14 @@ +#include +#include +#include + +#define HEAP_SIZE (1024 * 1024) + +static uint8_t heap[HEAP_SIZE]; +static size_t heap_offset = 0; + +void* jalloc(size_t size) { + void* ptr = &heap[heap_offset]; + heap_offset += size; + return ptr; +} diff --git a/src/jalloc.h b/src/jalloc.h new file mode 100644 index 0000000..cf657ed --- /dev/null +++ b/src/jalloc.h @@ -0,0 +1,3 @@ +#include + +void* jalloc(size_t size); diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..0ee2909 --- /dev/null +++ b/src/main.c @@ -0,0 +1,106 @@ +#include "jalloc.h" +#include +#include +#include +#include + +void test_jalloc(void) { + int passed = 0, failed = 0; + + // --- Test 1: Basic write/read --- + { + size_t size = 64; + uint8_t *buf = (uint8_t *)jalloc(size); + if (!buf) { + printf("[FAIL] Test 1: jalloc returned NULL for size %zu\n", size); + failed++; + } else { + for (size_t i = 0; i < size; i++) buf[i] = (uint8_t)i; + int ok = 1; + for (size_t i = 0; i < size; i++) { + if (buf[i] != (uint8_t)i) { ok = 0; break; } + } + printf("[%s] Test 1: sequential byte pattern (%zu bytes)\n", + ok ? "PASS" : "FAIL", size); + ok ? passed++ : failed++; + } + } + + // --- Test 2: Multiple independent allocations don't overlap --- + { + size_t size = 128; + uint8_t *a = (uint8_t *)jalloc(size); + uint8_t *b = (uint8_t *)jalloc(size); + if (!a || !b) { + printf("[FAIL] Test 2: jalloc returned NULL\n"); + failed++; + } else { + memset(a, 0xAA, size); + memset(b, 0xBB, size); + int ok = 1; + for (size_t i = 0; i < size; i++) { + if (a[i] != 0xAA || b[i] != 0xBB) { ok = 0; break; } + } + printf("[%s] Test 2: two allocations don't overlap (%zu bytes each)\n", + ok ? "PASS" : "FAIL", size); + ok ? passed++ : failed++; + } + } + + // --- Test 3: Small allocation (1 byte) --- + { + uint8_t *buf = (uint8_t *)jalloc(1); + if (!buf) { + printf("[FAIL] Test 3: jalloc returned NULL for 1 byte\n"); + failed++; + } else { + *buf = 0x42; + int ok = (*buf == 0x42); + printf("[%s] Test 3: single byte allocation\n", ok ? "PASS" : "FAIL"); + ok ? passed++ : failed++; + } + } + + // --- Test 4: Large allocation --- + { + size_t size = 1024 * 1024; // 1 MiB + uint8_t *buf = (uint8_t *)jalloc(size); + if (!buf) { + printf("[FAIL] Test 4: jalloc returned NULL for %zu bytes\n", size); + failed++; + } else { + memset(buf, 0xCD, size); + int ok = 1; + for (size_t i = 0; i < size; i++) { + if (buf[i] != 0xCD) { ok = 0; break; } + } + printf("[%s] Test 4: large allocation (%zu bytes)\n", + ok ? "PASS" : "FAIL", size); + ok ? passed++ : failed++; + } + } + + // --- Test 5: String write/read --- + { + const char *msg = "Hello, jalloc!"; + size_t size = strlen(msg) + 1; + char *buf = (char *)jalloc(size); + if (!buf) { + printf("[FAIL] Test 5: jalloc returned NULL\n"); + failed++; + } else { + strcpy(buf, msg); + int ok = (strcmp(buf, msg) == 0); + printf("[%s] Test 5: string integrity (\"%s\")\n", + ok ? "PASS" : "FAIL", buf); + ok ? passed++ : failed++; + } + } + + printf("\n=== Results: %d passed, %d failed ===\n", passed, failed); +} + +int main(void) { + test_jalloc(); + return 0; +}