import os import sys import subprocess import time from pathlib import Path from rich.console import Console from glob import glob # Import menus do TUI sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from tui import menu_python, menu_iso, menu_libs # pylint: disable=wrong-import-position, import-error console = Console() def corrigir_kernel_headers(linux_dir, env): """ Corrige headers incompatíveis com C23 em vários caminhos. Adiciona guards para typedef bool / enum { false, true }. """ console.print("[yellow]→ Patching kernel headers for C23 compatibility...[/]") header_candidates = [ "include/linux/stddef.h", "include/linux/types.h", "include/uapi/linux/stddef.h", "include/uapi/linux/types.h", "arch/x86/include/asm/stddef.h", "arch/x86/include/asm/types.h", ] # Antes de aplicar os patches: safe_run(["make", "prepare"], env=env) safe_run(["make", "scripts"], env=env) for relpath in header_candidates: path = linux_dir / relpath if not path.exists(): continue content = path.read_text() patched = False # Corrige enum false/true if "false" in content and "__bool_true_false_are_defined" not in content: content = content.replace( "enum {\n false = 0,\n true = 1\n};", "#ifndef __bool_true_false_are_defined\n" "enum {\n false = 0,\n true = 1\n};\n" "#define __bool_true_false_are_defined 1\n#endif" ) patched = True # Corrige typedef _Bool if "typedef _Bool" in content and "__STDC_VERSION__" not in content: content = content.replace( "typedef _Bool bool;", "#ifndef __cplusplus\n" "# if __STDC_VERSION__ < 202000L\n" "typedef _Bool bool;\n" "# endif\n" "#endif" ) patched = True if patched: path.write_text(content) console.print(f"[green]✔ Patched:[/] {relpath}") # Corrige Makefile do boot boot_makefile = linux_dir / "arch/x86/boot/Makefile" if boot_makefile.exists(): with open(boot_makefile, "a") as f: f.write("\n# C23 fix\nccflags-y += -std=gnu11 -Wno-error\n") console.print("[cyan]→ Applied local Makefile override for boot code (C23 fix)[/]") # Corrige Makefile do boot/compressed boot_compr_makefile = linux_dir / "arch/x86/boot/compressed/Makefile" if boot_compr_makefile.exists(): with open(boot_compr_makefile, "a") as f: f.write("\n# C23 fix\nccflags-y += -std=gnu11 -Wno-error\n") console.print("[cyan]→ Applied local Makefile override for boot/compressed code (C23 fix)[/]") # Corrige Makefile do EFI stub efi_makefile = linux_dir / "drivers/firmware/efi/libstub/Makefile" if efi_makefile.exists(): with open(efi_makefile, "a") as f: f.write("\n# C23 fix\nccflags-y += -std=gnu11 -Wno-error\n") console.print("[cyan]→ Applied local Makefile override for EFI stub (C23 fix)[/]") def corrigir_kernel_overrides(cross_compile): env = { **os.environ, "ARCH": "x86_64", "CROSS_COMPILE": cross_compile, "LOCALVERSION": "-nfdos", } console.print("[yellow]→ Aplicando overrides de configuração...[/yellow]") # Essenciais do sistema base essentials = [ "CONFIG_PRINTK", "CONFIG_TTY", "CONFIG_SERIAL_8250", "CONFIG_SERIAL_8250_CONSOLE", "CONFIG_SERIAL_EARLYCON", "CONFIG_DEVTMPFS", "CONFIG_DEVTMPFS_MOUNT", "CONFIG_BLK_DEV_INITRD", "CONFIG_TMPFS", "CONFIG_PROC_FS", "CONFIG_SYSFS", # EXT4 base + alias para ext2 "CONFIG_EXT4_FS", "CONFIG_EXT4_USE_FOR_EXT2", "CONFIG_MCORE2", #"CONFIG_EXT4_FS_POSIX_ACL", #"CONFIG_EXT4_FS_SECURITY", ] # VirtIO (para bloco, rede, PCI) virtio = [ "CONFIG_PCI", "CONFIG_VIRTIO", "CONFIG_VIRTIO_MENU", "CONFIG_VIRTIO_PCI", "CONFIG_VIRTIO_PCI_LEGACY", "CONFIG_VIRTIO_BLK", "CONFIG_VIRTIO_NET", "CONFIG_VIRTIO_CONSOLE", "CONFIG_VIRTIO_INPUT", "CONFIG_VIRTIO_MMIO", "CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES", "CONFIG_BLK_MQ_VIRTIO", "CONFIG_BLOCK", "CONFIG_BLK_DEV", "CONFIG_BLOCK_LEGACY_AUTOLOAD", "CONFIG_EXPORTFS_BLOCK_OPS", "CONFIG_MSDOS_PARTITION", # assegura parsing de tabela de partições ] # Debug e early printk debug = [ "CONFIG_DEBUG_KERNEL", "CONFIG_EARLY_PRINTK", "CONFIG_SERIAL_8250_PNP", "CONFIG_DEBUG_INFO_NONE", # evita symbols extras ] # Desativa gráficos e Garante compatibilidade máxima de CPU (para manter o kernel leve, limpo e evitar CR4 panics) disable_graphics = [ "CONFIG_VT", "CONFIG_VT_CONSOLE", "CONFIG_VGA_CONSOLE", "CONFIG_FRAMEBUFFER_CONSOLE", "CONFIG_DUMMY_CONSOLE", "CONFIG_FB", "CONFIG_DRM", "CONFIG_DRM_I915", "CONFIG_LOGO", "CONFIG_X86_PAE", "CONFIG_X86_5LEVEL", "CONFIG_RANDOMIZE_BASE", "CONFIG_RETPOLINE", "CONFIG_SMAP", "CONFIG_SMEP", "CONFIG_PAGE_TABLE_ISOLATION", ] # Habilita todos os conjuntos for opt in essentials + virtio + debug: safe_run(["scripts/config", "--enable", opt], env=env) # garante que o driver não é módulo for opt in [ "CONFIG_VIRTIO_BLK=m", "CONFIG_VIRTIO_PCI=m", "CONFIG_VIRTIO_PCI_LEGACY=m", ]: safe_run(["scripts/config", "--disable", opt], env=env) # Desativa o que não precisamos for opt in disable_graphics: safe_run(["scripts/config", "--disable", opt], env=env) # Formatos binários básicos for opt in [ "CONFIG_BINFMT_ELF", "CONFIG_BINFMT_SCRIPT", ]: safe_run(["scripts/config", "--enable", opt], env=env) # Regera configuração coerente safe_run(["make", "olddefconfig"], env=env) console.print("[green]✔ Overrides aplicados com sucesso.[/green]") def safe_run(cmd, **kwargs): """Executa um comando e mostra logs legíveis no TUI.""" console.print(f"[dim]{' '.join(cmd) if isinstance(cmd, list) else cmd}[/dim]") subprocess.run(cmd, check=True, **kwargs) def limpar_rootfs(nfdos_dir): rootfs = nfdos_dir / "rootfs" initramfs = nfdos_dir / "initramfs.cpio.gz" if rootfs.exists(): console.print("[yellow]🧹 Limpando RootFS anterior...[/yellow]") safe_run(f"rm -rf {rootfs}", shell=True) if initramfs.exists(): console.print("[yellow]🧹 Removendo initramfs anterior...[/yellow]") safe_run(f"rm -f {initramfs}", shell=True) def run(): while True: console.clear() console.rule("[bold yellow]Kernel / BusyBox / Python[/bold yellow]") console.print("1. Obter código-fonte do Kernel Linux") console.print("2. Compilar Kernel com Toolchain NFDOS") console.print("3. Obter e compilar BusyBox") console.print("4. Compilar Python estatico com Toolchain NFDOS") console.print("5. Obter e compilar Bibliotecas do Neurotron") console.print("6. Montar RootFS e gerar imagem bootável") console.print("7. Gerar imagem ISO e iniciar o NFDOS no QEMU") console.print("0. Voltar") choice = console.input("\n[cyan]nfdos> [/cyan]") base_dir = Path(__file__).resolve().parents[1] kernel_dir = base_dir / "_nfdos" / "kernel" linux_dir = kernel_dir / "linux" nfdos_dir = base_dir / "_nfdos" busybox_dir = nfdos_dir / "busybox" rootfs_dir = nfdos_dir / "rootfs" toolchain_prefix = Path.home() / "x-tools" / "x86_64-nfdos-linux-musl" cross_compile = f"{toolchain_prefix}/bin/x86_64-nfdos-linux-musl-" linux_repo = "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git" busybox_repo = "git://busybox.net/busybox.git" env = { **os.environ, "ARCH": "x86_64", "CROSS_COMPILE": cross_compile, "CFLAGS": "-std=gnu11 -fcommon -Wno-error", "LOCALVERSION": "-nfdos", "KCFLAGS": "-std=gnu11 -fcommon -Wno-error", "HOSTCFLAGS": "-std=gnu11 -fcommon -Wno-error", "KBUILD_CFLAGS": "-std=gnu11 -fcommon -Wno-error", "KBUILD_AFLAGS": "-std=gnu11 -fcommon", "KBUILD_CPPFLAGS": "-std=gnu11 -fcommon", "KCFLAGS_FOR_HOST": "-std=gnu11 -Wno-error", } if choice == "1": kernel_dir.mkdir(parents=True, exist_ok=True) os.chdir(kernel_dir) console.print("[yellow]Baixando código-fonte do Kernel Linux...[/yellow]") if not linux_dir.exists(): safe_run(["git", "clone", "--depth=1", "--branch", "v6.12", linux_repo, "linux"]) else: os.chdir(linux_dir) safe_run(["git", "pull"]) console.print("[green]✔ Kernel atualizado![/green]") elif choice == "2": os.chdir(linux_dir) console.print("[cyan]Compilando Kernel Linux...[/cyan]") # Verificação do compilador if not Path(cross_compile + "gcc").exists(): console.print("[red]✗ Toolchain não encontrada![/red]") console.print(f"Esperado: {cross_compile}gcc") time.sleep(2) continue # Passo 1 — Limpar tudo antes de começar safe_run(["make", "mrproper"], env=env) # Passo 2 — Baixar configuração base mínima safe_run(["make", "allnoconfig"], env=env) # Passo 3 — Aplicar patches C23 aos headers corrigir_kernel_headers(linux_dir, env) # Passo 4 — Aplicar overrides minimalistas (serial-only, sem VGA) corrigir_kernel_overrides(cross_compile) # Passo 5 — Regerar dependências e .config safe_run(["make", "olddefconfig"], env=env) # Passo 6 — Compilar safe_run(["make", "-j", str(os.cpu_count())], env=env) console.print("[green]✔ Kernel compilado com sucesso![/green]") elif choice == "3": busybox_dir.mkdir(parents=True, exist_ok=True) os.chdir(busybox_dir) console.print("[yellow]📦 Baixando e compilando BusyBox...[/yellow]") if not (busybox_dir / ".git").exists(): safe_run(["git", "clone", "--depth=1", busybox_repo, "-b", "master", "."], env=env) # 🧹 Limpeza preventiva se houver restos de builds anteriores if (busybox_dir / ".config").exists(): console.print("[cyan]🧼 Limpando build antigo (make mrproper)...[/cyan]") safe_run(["make", "mrproper"], env=env) console.print("[cyan]🧩 Gerando configuração base (defconfig)...[/cyan]") safe_run(["make", "defconfig"], env=env) console.print("[cyan]🧠 Aplicando overrides de configuração (persistência + EXT4)...[/cyan]") config_updates = { "CONFIG_STATIC": "y", "CONFIG_MKFS": "y", "CONFIG_FEATURE_MKFS_EXT2": "y", "CONFIG_FEATURE_MKFS_EXT3": "y", "CONFIG_FEATURE_MKFS_EXT4": "y", "CONFIG_MOUNT": "y", "CONFIG_UMOUNT": "y", "CONFIG_BLKID": "y", "CONFIG_LSBLK": "y", "CONFIG_FSCK": "y", "CONFIG_FEATURE_MOUNT_HELPERS": "y", "CONFIG_FEATURE_MOUNT_FSTAB": "y", "CONFIG_FEATURE_VOLUMEID": "y", "CONFIG_FEATURE_VOLUMEID_EXT": "y", "CONFIG_FEATURE_VOLUMEID_FAT": "y", "CONFIG_FEATURE_VOLUMEID_EXFAT": "y", "CONFIG_FEATURE_VOLUMEID_NTFS": "y", "CONFIG_FEATURE_VOLUMEID_LINUXSWAP": "y", "CONFIG_FEATURE_VOLUMEID_BTRFS": "y", "CONFIG_FEATURE_VOLUMEID_XFS": "y", "CONFIG_FEATURE_VOLUMEID_ISO9660": "y", } for key, value in config_updates.items(): safe_run([ "sed", "-i", f"s/^# {key} is not set/{key}={value}/; t; s/^{key}=.*/{key}={value}/; t; $a\\{key}={value}", ".config" ], env=env) # 🧠 Shell e utilitários básicos safe_run(["sed", "-i", "s/^CONFIG_TC=.*/# CONFIG_TC is not set/", ".config"], env=env) safe_run(["sed", "-i", "s/^# CONFIG_ASH is not set/CONFIG_ASH=y/", ".config"], env=env) safe_run(["sed", "-i", "s/^# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set/CONFIG_ASH_OPTIMIZE_FOR_SIZE=y/", ".config"], env=env) safe_run(["sed", "-i", "s/^# CONFIG_FEATURE_SH_STANDALONE is not set/CONFIG_FEATURE_SH_STANDALONE=y/", ".config"], env=env) console.print("[yellow]🔧 Regerando configuração coerente (make oldconfig)...[/yellow]") safe_run(["make", "oldconfig"], env=env) console.print("[yellow]🔨 Compilando BusyBox (estático, com EXT4 e ferramentas de disco)...[/yellow]") safe_run(["make", "-j", str(os.cpu_count())], env=env) safe_run(["make", "install"], env=env) console.print("[green]✔ BusyBox compilado e instalado com sucesso![/green]") elif choice == "4": menu_python.run() elif choice == "5": menu_libs.run() elif choice == "6": os.chdir(nfdos_dir) console.print("[cyan]Montando RootFS e gerando imagem bootável...[/cyan]") limpar_rootfs(nfdos_dir) rootfs_dir = nfdos_dir / "rootfs" rootfs_dir.mkdir(exist_ok=True) # Estrutura de diretórios básica for d in ["bin", "sbin", "etc", "proc", "sys", "usr/bin", "usr/sbin", "opt/kernel"]: (rootfs_dir / d).mkdir(parents=True, exist_ok=True) # Garante os device nodes no initramfs safe_run(f"mkdir -p {rootfs_dir}/dev", shell=True) safe_run(f"sudo mknod -m 600 {rootfs_dir}/dev/console c 5 1", shell=True) safe_run(f"sudo mknod -m 666 {rootfs_dir}/dev/null c 1 3", shell=True) safe_run(f"sudo mknod -m 600 {rootfs_dir}/dev/ttyS0 c 4 64", shell=True) safe_run(f"sudo mknod -m 666 {rootfs_dir}/dev/random c 1 8", shell=True) safe_run(f"sudo mknod -m 666 {rootfs_dir}/dev/urandom c 1 9", shell=True) # Copiar o BusyBox instalado para o rootfs console.print("[blue]🔗 Copiando BusyBox para o rootfs...[/blue]") safe_run(f"cp -a {busybox_dir}/_install/* {rootfs_dir}/", shell=True) safe_run(["chmod", "-R", "755", str(rootfs_dir / "bin")]) console.print("[green]✔ BusyBox incluído no rootfs![/green]") # Inserir a Lib Python (TODO: No NFDOS V0.2 build “fully frozen”) safe_run(f"mkdir -p {rootfs_dir}/usr/lib/python3.13", shell=True) safe_run(f"rsync -av --exclude='test' --exclude='idlelib' --exclude='tkinter' \ {nfdos_dir}/cpython/Lib/ {rootfs_dir}/usr/lib/python3.13/", shell=True) # --- Copiar binário Python estático para o RootFS --- python_bin = nfdos_dir / "python" usr_bin = rootfs_dir / "usr" / "bin" usr_bin.mkdir(parents=True, exist_ok=True) if python_bin.exists(): console.print("[yellow]→ Adicionando Python estático ao RootFS...[/yellow]") safe_run(f"cp {python_bin} {usr_bin}/python3", shell=True) else: console.print("[red]⚠ Python estático não encontrado em nfdos/python — compilar antes.[/red]") # Inserir Neurotron (se existir) opt_kernel = rootfs_dir / "opt" / "kernel" opt_kernel.mkdir(parents=True, exist_ok=True) # ============================================ # Instalar Neurotron nativamente no RootFS # ============================================ neurotron_src = kernel_dir / "neurotron" / "src" neurotron_data = kernel_dir / "neurotron" / "data" neurotron_bin = kernel_dir / "neurotron" / "neurotron" # gerado pelo autotools neurotron_root = opt_kernel / "neurotron" neurotron_root.mkdir(parents=True, exist_ok=True) if neurotron_src.exists(): console.print("[yellow]→ Instalando código-fonte do Neurotron...[/yellow]") safe_run(f"cp -r {neurotron_src} {neurotron_root}/", shell=True) else: console.print("[red]✗ Código-fonte do Neurotron não encontrado.[/red]") if neurotron_data.exists(): console.print("[yellow]→ Instalando diretório data/ do Neurotron...[/yellow]") safe_run(f"cp -r {neurotron_data} {neurotron_root}/", shell=True) # Instalar binário /usr/bin/neurotron usr_bin = rootfs_dir / "usr" / "bin" usr_bin.mkdir(parents=True, exist_ok=True) if neurotron_bin.exists(): console.print("[yellow]→ Instalando wrapper /usr/bin/neurotron[/yellow]") safe_run(f"cp {neurotron_bin} {usr_bin}/neurotron", shell=True) safe_run(f"chmod +x {usr_bin}/neurotron", shell=True) else: console.print("[red]✗ Wrapper 'neurotron' não encontrado — corre 'make' em kernel/neurotron[/red]") # Instalar terminais lterm_dir = rootfs_dir / "usr" / "share" / "terminfo" / "l" lterm_dir.mkdir(parents=True, exist_ok=True) safe_run(f"cp {nfdos_dir}/linux {lterm_dir}/", shell=True) xterm_dir = rootfs_dir / "usr" / "share" / "terminfo" / "x" xterm_dir.mkdir(parents=True, exist_ok=True) safe_run(f"cp {nfdos_dir}/xterm {xterm_dir}/", shell=True) vterm_dir = rootfs_dir / "usr" / "share" / "terminfo" / "v" vterm_dir.mkdir(parents=True, exist_ok=True) safe_run(f"cp {nfdos_dir}/vt100 {vterm_dir}/", shell=True) # Instalar libs externas do Neurotron (se existirem) libs_dir = nfdos_dir / "libs" site_packages = rootfs_dir / "usr/lib/python3.13/site-packages" site_packages.mkdir(parents=True, exist_ok=True) lib_files = [] for ext in ("*.whl", "*.tar.gz", "*.zip", "*.tgz"): lib_files.extend(glob(f"{libs_dir}/{ext}")) if lib_files: libs_str = " ".join(lib_files) console.print("[cyan]→ Instalando bibliotecas externas do Neurotron...[/cyan]") safe_run( f"pip install --no-deps --target={site_packages} {libs_str}", shell=True ) else: console.print("[yellow]⚠️ Nenhuma biblioteca externa encontrada em libs/.[/yellow]") # Script init com chamada ao Neurotron init_file = rootfs_dir / "init" initramfs_file = nfdos_dir / "initramfs.cpio.gz" safe_run(f"cd {rootfs_dir} && cp ../init .", shell=True) # TODO: usar f.write para automatizar o init safe_run(["chmod", "+x", str(init_file)]) # Gerar initramfs console.print("[blue]Gerando initramfs.cpio.gz...[/blue]") if initramfs_file.exists(): safe_run(f"rm {initramfs_file}", shell=True) safe_run(f"cd {rootfs_dir} && find . | cpio -H newc -o | gzip > {initramfs_file}", shell=True) console.print("[green]✔ RootFS criado e compactado![/green]") # 🧠 Validação do init antes de gerar o initramfs console.print("[blue]🔍 Verificando integridade do init...[/blue]") # Garante que o init existe if not init_file.exists(): console.print(f"[red]✗ Ficheiro init não encontrado em {init_file}[/red]") raise SystemExit(1) # Mostra onde ele está dentro do initramfs safe_run(f'cd {nfdos_dir} && lsinitramfs initramfs.cpio.gz | grep -E "(^|/)init$"', shell=True) # Mostra tipo e encoding console.print("[cyan]→ Verificando tipo de ficheiro...[/cyan]") safe_run(f'file {init_file}', shell=True) # Mostra primeiras linhas (para confirmar shebang) console.print("[cyan]→ Conteúdo inicial do init (máx 10 linhas)...[/cyan]") safe_run(f'head -n 10 {init_file}', shell=True) # Sanitização: remove BOM e CRLF console.print("[blue]→ Removendo BOM e CRLF (normalização universal)...[/blue]") safe_run(f"sed -i '1s/^\\xEF\\xBB\\xBF//' {init_file}", shell=True) safe_run(f"sed -i 's/\\r$//' {init_file}", shell=True) # Confirma permissões console.print("[cyan]→ Ajustando permissões de execução...[/cyan]") safe_run(f'chmod 0755 {init_file}', shell=True) # Segunda verificação do tipo console.print("[green]✔ Ficheiro final após normalização:[/green]") safe_run(f'file {init_file}', shell=True) # Validação do shebang e encoding console.print("[blue]→ Testando assinatura e line endings...[/blue]") safe_run(f"head -n1 {init_file} | od -c", shell=True) safe_run(f"grep -Ua 'bin/sh' {init_file} | head -n1", shell=True) console.print("[green]✔ init validado e pronto para inclusão no initramfs[/green]") # Criar disco (se não existir) data_disk = nfdos_dir / "nfdos_data.img" if not data_disk.exists(): console.print("[cyan]💽 Criando disco persistente (hipocampo físico)...[/cyan]") safe_run(f"dd if=/dev/zero of={data_disk} bs=1M count=512", shell=True) else: console.print("[green]✔ Disco persistente já existe.[/green]") time.sleep(5) # Testar no QEMU bz_image = linux_dir / "arch" / "x86" / "boot" / "bzImage" data_disk = nfdos_dir / "nfdos_data.img" if bz_image.exists(): console.print("\n[bold blue]Iniciando QEMU (modo kernel direto)...[/bold blue]") # 🔒 Pergunta ao utilizador se deve permitir formatação automática do disco console.print( "\n[yellow]O Neurotron detetará o disco físico e poderá formatá-lo se estiver vazio.[/yellow]" ) console.print( "[bright_yellow]Deseja permitir formatação automática do disco?[/bright_yellow] " "([green]y[/green]/[red]N[/red]): ", end="" ) choice = input().strip().lower() allow_format = choice in ("y", "yes", "sim", "s") # 🧠 Monta a linha base do QEMU kernel_params = ( "console=ttyS0 earlyprintk=serial,ttyS0,115200 " "keep_bootcon loglevel=8" ) # 🧩 Injeta flag de formatação se permitido if allow_format: kernel_params += " nfdos_force_format=1" console.print("[green]✔ Formatação automática permitida neste boot.[/green]") else: console.print("[cyan]ℹ Formatação automática desativada (modo seguro).[/cyan]") qemu_cmd = ( f"qemu-system-x86_64 " f"-machine q35,accel=kvm " f"-cpu qemu64 " f"-kernel {bz_image} " f"-initrd {nfdos_dir}/initramfs.cpio.gz " f"-append '{kernel_params}' " f"-drive file={data_disk},if=virtio,format=raw " f"-m 1024 " f"-nographic " f"-no-reboot" ) # 🚀 Executa o QEMU safe_run(qemu_cmd, shell=True) else: console.print("[red]✗ bzImage não encontrado! Compile o kernel primeiro.[/red]") elif choice == "7": menu_iso.run() elif choice == "0": break else: console.print("[red]Opção inválida![/red]") time.sleep(1)