Files
Deshab-OS/build.ps1
T

743 lines
27 KiB
PowerShell
Raw Normal View History

$ErrorActionPreference = 'Stop'
$Root = Split-Path -Parent $MyInvocation.MyCommand.Path
$UtsmDir = Join-Path $Root 'CODE\UTSM'
$DskDir = Join-Path $Root 'CODE\dsk'
$SystemDir = Join-Path $Root 'SYSTEM'
$IsoDir = Join-Path $Root 'ISO'
$Output = Join-Path $SystemDir 'boot\utsm.elf'
$DskOutput = Join-Path $SystemDir 'system\deshab64\deshab.elf'
$ImagePath = Join-Path $IsoDir 'deshab.img'
$BuildTmp = Join-Path $Root '.build_tmp'
function Find-Tool($Name, $ExtraNames = @()) {
$names = @($Name) + $ExtraNames
foreach ($n in $names) {
$cmd = Get-Command $n -ErrorAction SilentlyContinue
if ($cmd) { return $cmd.Source }
}
$dirs = @(
'C:\Program Files\LLVM\bin',
'C:\Program Files (x86)\LLVM\bin',
'C:\LLVM\bin',
'D:\LLVM\bin',
'D:\Program Files\LLVM\bin',
'D:\Code\LLVM\bin',
'C:\msys64\clang64\bin',
'C:\msys64\mingw64\bin',
'C:\mingw64\bin'
)
foreach ($dir in $dirs) {
foreach ($n in $names) {
$candidate = Join-Path $dir $n
if (Test-Path $candidate) { return $candidate }
if (-not $n.EndsWith('.exe')) {
$candidateExe = Join-Path $dir "$n.exe"
if (Test-Path $candidateExe) { return $candidateExe }
}
}
}
return $null
}
function Build-DkmDriver([string]$SourcePath, [string]$OutputPath) {
$obj = Join-Path $BuildTmp (([System.IO.Path]::GetFileNameWithoutExtension($SourcePath)) + '.o')
$outDir = Split-Path -Parent $OutputPath
if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Force -Path $outDir | Out-Null }
& $clang -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fno-pic -fno-pie -mcmodel=kernel -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -O2 -c $SourcePath -o $obj
if ($LASTEXITCODE -ne 0) { throw "failed to compile DKM driver: $SourcePath" }
& $lld -r -o $OutputPath $obj
if ($LASTEXITCODE -ne 0) { throw "failed to link DKM driver: $OutputPath" }
Write-Host "[build] DKM driver Output: $OutputPath"
}
function Set-Le16([byte[]]$Buffer, [int]$Offset, [int]$Value) {
$Buffer[$Offset] = [byte]($Value -band 0xff)
$Buffer[$Offset + 1] = [byte](($Value -shr 8) -band 0xff)
}
function Set-Le32([byte[]]$Buffer, [int]$Offset, [UInt64]$Value) {
$Buffer[$Offset] = [byte]($Value -band 0xff)
$Buffer[$Offset + 1] = [byte](($Value -shr 8) -band 0xff)
$Buffer[$Offset + 2] = [byte](($Value -shr 16) -band 0xff)
$Buffer[$Offset + 3] = [byte](($Value -shr 24) -band 0xff)
}
function Set-Le64([byte[]]$Buffer, [int]$Offset, [UInt64]$Value) {
for ($i = 0; $i -lt 8; $i++) {
$Buffer[$Offset + $i] = [byte](($Value -shr ($i * 8)) -band 0xff)
}
}
function Write-At($Stream, [Int64]$Offset, [byte[]]$Data) {
[void]$Stream.Seek($Offset, [System.IO.SeekOrigin]::Begin)
$Stream.Write($Data, 0, $Data.Length)
}
function Get-Crc32([byte[]]$Data) {
$crc = [uint32]4294967295
foreach ($b in $Data) {
$crc = $crc -bxor [uint32]$b
for ($i = 0; $i -lt 8; $i++) {
if (($crc -band 1) -ne 0) {
$crc = [uint32](($crc -shr 1) -bxor 0xedb88320)
} else {
$crc = [uint32]($crc -shr 1)
}
}
}
return [uint32]($crc -bxor [uint32]4294967295)
}
function Test-ShortFatName([string]$Name) {
if ($Name -cmatch '[a-z]') { return $false }
$parts = $Name.Split('.')
if ($parts.Count -gt 2) { return $false }
if ($parts[0].Length -lt 1 -or $parts[0].Length -gt 8) { return $false }
if ($parts.Count -eq 2 -and $parts[1].Length -gt 3) { return $false }
return ($Name -cmatch '^[A-Z0-9_]+$') -or ($Name -cmatch '^[A-Z0-9_]+\.[A-Z0-9_]+$')
}
function New-ShortFatName([string]$LongName, $Used) {
$upper = $LongName.ToUpperInvariant()
if ((Test-ShortFatName $upper) -and -not $Used.ContainsKey($upper)) {
$Used[$upper] = $true
$parts = $upper.Split('.')
$base = $parts[0].PadRight(8, ' ')
$ext = ''
if ($parts.Count -eq 2) { $ext = $parts[1] }
return ($base + $ext.PadRight(3, ' '))
}
$dot = $upper.LastIndexOf('.')
if ($dot -ge 0) {
$baseRaw = $upper.Substring(0, $dot)
$extRaw = $upper.Substring($dot + 1)
} else {
$baseRaw = $upper
$extRaw = ''
}
$baseClean = ($baseRaw -replace '[^A-Z0-9]', '')
$extClean = ($extRaw -replace '[^A-Z0-9]', '')
if ($baseClean.Length -eq 0) { $baseClean = 'FILE' }
if ($extClean.Length -gt 3) { $extClean = $extClean.Substring(0, 3) }
for ($i = 1; $i -lt 1000; $i++) {
$suffix = "~$i"
$take = [Math]::Min(8 - $suffix.Length, $baseClean.Length)
$candidateBase = $baseClean.Substring(0, $take) + $suffix
$display = $candidateBase
if ($extClean.Length -gt 0) { $display = "$candidateBase.$extClean" }
if (-not $Used.ContainsKey($display)) {
$Used[$display] = $true
return ($candidateBase.PadRight(8, ' ') + $extClean.PadRight(3, ' '))
}
}
throw "Unable to generate FAT short name for $LongName"
}
function Get-ShortNameBytes([string]$ShortName) {
return [System.Text.Encoding]::ASCII.GetBytes($ShortName)
}
function Get-LfnChecksum([byte[]]$ShortBytes) {
$sum = 0
foreach ($b in $ShortBytes) {
$sum = (((($sum -band 1) * 0x80) + ($sum -shr 1) + $b) -band 0xff)
}
return [byte]$sum
}
function New-LfnEntry([string]$Chunk, [int]$Order, [byte]$Checksum) {
$entry = [byte[]]::new(32)
for ($i = 0; $i -lt 32; $i++) { $entry[$i] = 0xff }
$entry[0] = [byte]$Order
$entry[11] = 0x0f
$entry[12] = 0x00
$entry[13] = $Checksum
$entry[26] = 0x00
$entry[27] = 0x00
$positions = @(1,3,5,7,9,14,16,18,20,22,24,28,30)
$chars = $Chunk.ToCharArray()
for ($i = 0; $i -lt $positions.Count; $i++) {
$pos = $positions[$i]
if ($i -lt $chars.Length) {
$code = [int][char]$chars[$i]
$entry[$pos] = [byte]($code -band 0xff)
$entry[$pos + 1] = [byte](($code -shr 8) -band 0xff)
} elseif ($i -eq $chars.Length) {
$entry[$pos] = 0x00
$entry[$pos + 1] = 0x00
}
}
return $entry
}
function New-ShortEntry([string]$ShortName, [byte]$Attr, [int]$Cluster, [uint32]$Size) {
$entry = [byte[]]::new(32)
$nameBytes = Get-ShortNameBytes $ShortName
[Array]::Copy($nameBytes, 0, $entry, 0, 11)
$entry[11] = $Attr
Set-Le16 $entry 20 (($Cluster -shr 16) -band 0xffff)
Set-Le16 $entry 26 ($Cluster -band 0xffff)
Set-Le32 $entry 28 $Size
return $entry
}
function Add-DirectoryEntry($Entries, [string]$Name, [byte]$Attr, [int]$Cluster, [uint32]$Size, $UsedNames) {
$shortName = New-ShortFatName $Name $UsedNames
$shortBytes = Get-ShortNameBytes $shortName
$upper = $Name.ToUpperInvariant()
$needsLfn = -not (Test-ShortFatName $upper) -or (($shortName.Trim() -replace ' +', '') -ne ($upper -replace '\.', ''))
if ($needsLfn) {
$checksum = Get-LfnChecksum $shortBytes
$chunks = @()
for ($i = 0; $i -lt $Name.Length; $i += 13) {
$len = [Math]::Min(13, $Name.Length - $i)
$chunks += $Name.Substring($i, $len)
}
for ($i = $chunks.Count; $i -ge 1; $i--) {
$order = $i
if ($i -eq $chunks.Count) { $order = $order -bor 0x40 }
[void]$Entries.Add((New-LfnEntry $chunks[$i - 1] $order $checksum))
}
}
[void]$Entries.Add((New-ShortEntry $shortName $Attr $Cluster $Size))
}
function Get-GuidBytes([string]$GuidString) {
return ([Guid]$GuidString).ToByteArray()
}
function New-GptHeader([UInt64]$CurrentLba, [UInt64]$BackupLba, [UInt64]$FirstUsable, [UInt64]$LastUsable, [byte[]]$DiskGuid, [UInt64]$EntriesLba, [uint32]$EntryCount, [uint32]$EntrySize, [uint32]$EntriesCrc) {
$header = [byte[]]::new(512)
[Array]::Copy([System.Text.Encoding]::ASCII.GetBytes('EFI PART'), 0, $header, 0, 8)
Set-Le32 $header 8 0x00010000
Set-Le32 $header 12 92
Set-Le64 $header 24 $CurrentLba
Set-Le64 $header 32 $BackupLba
Set-Le64 $header 40 $FirstUsable
Set-Le64 $header 48 $LastUsable
[Array]::Copy($DiskGuid, 0, $header, 56, 16)
Set-Le64 $header 72 $EntriesLba
Set-Le32 $header 80 $EntryCount
Set-Le32 $header 84 $EntrySize
Set-Le32 $header 88 $EntriesCrc
$crcData = [byte[]]::new(92)
[Array]::Copy($header, 0, $crcData, 0, 92)
Set-Le32 $crcData 16 0
$crc = Get-Crc32 $crcData
Set-Le32 $header 16 $crc
return $header
}
function New-GptFat32Image([string]$SourceDir, [string]$ImagePath, [int]$SizeMB = 128) {
$bytesPerSector = 512
$totalSectors = [UInt64](($SizeMB * 1024 * 1024) / $bytesPerSector)
$lastLba = $totalSectors - 1
$entryCount = [uint32]128
$entrySize = [uint32]128
$entrySectors = [UInt64](($entryCount * $entrySize) / $bytesPerSector)
$firstUsable = [UInt64](2 + $entrySectors)
$lastUsable = [UInt64]($lastLba - $entrySectors - 1)
$partStart = [UInt64]2048
$partEnd = $lastUsable
$partSectors = [UInt64]($partEnd - $partStart + 1)
$partitionOffset = [Int64]($partStart * $bytesPerSector)
$sectorsPerCluster = 1
$reservedSectors = 32
$numFats = 2
$fatSectors = 1
do {
$dataSectors = [Int64]$partSectors - $reservedSectors - ($numFats * $fatSectors)
$clusterCount = [int]($dataSectors / $sectorsPerCluster)
$neededFatSectors = [int]((($clusterCount + 2) * 4 + $bytesPerSector - 1) / $bytesPerSector)
if ($neededFatSectors -eq $fatSectors) { break }
$fatSectors = $neededFatSectors
} while ($true)
if ($clusterCount -lt 65525) {
throw "Invalid FAT32 cluster count: $clusterCount"
}
$fatOffset = [Int64]($partitionOffset + $reservedSectors * $bytesPerSector)
$dataOffset = [Int64]($partitionOffset + ($reservedSectors + $numFats * $fatSectors) * $bytesPerSector)
$clusterSize = $sectorsPerCluster * $bytesPerSector
$fat = [UInt32[]]::new($clusterCount + 2)
$fat[0] = 0x0ffffff8
$fat[1] = 0x0fffffff
$fat[2] = 0x0fffffff
$script:fatNextCluster = 3
function Allocate-Chain([Int64]$ByteCount) {
$needed = [Math]::Max(1, [int](($ByteCount + $clusterSize - 1) / $clusterSize))
$first = $script:fatNextCluster
for ($i = 0; $i -lt $needed; $i++) {
$cluster = $script:fatNextCluster
$script:fatNextCluster++
if ($script:fatNextCluster -ge $fat.Length) { throw 'FAT32 image is too small' }
if ($i -eq $needed - 1) {
$fat[$cluster] = 0x0fffffff
} else {
$fat[$cluster] = [UInt32]($cluster + 1)
}
}
return $first
}
function Get-ClusterOffset([int]$Cluster) {
return [Int64]($dataOffset + (($Cluster - 2) * $clusterSize))
}
function Write-FileContent($Stream, [string]$Path, [int]$FirstCluster) {
$bytes = [System.IO.File]::ReadAllBytes($Path)
$remaining = $bytes.Length
$srcOffset = 0
$cluster = $FirstCluster
while ($remaining -gt 0) {
$chunk = [Math]::Min($clusterSize, $remaining)
[void]$Stream.Seek((Get-ClusterOffset $cluster), [System.IO.SeekOrigin]::Begin)
$Stream.Write($bytes, $srcOffset, $chunk)
$srcOffset += $chunk
$remaining -= $chunk
if ($remaining -gt 0) { $cluster = $fat[$cluster] }
}
}
function Write-Directory($Stream, [string]$Path, [int]$SelfCluster, [int]$ParentCluster, [bool]$IsRoot) {
$entries = [System.Collections.ArrayList]::new()
$used = @{}
if (-not $IsRoot) {
[void]$entries.Add((New-ShortEntry '. ' 0x10 $SelfCluster 0))
[void]$entries.Add((New-ShortEntry '.. ' 0x10 $ParentCluster 0))
}
$items = Get-ChildItem -LiteralPath $Path -Force | Where-Object { $_.Name -notlike '*.tmp*' } | Sort-Object @{Expression={$_.PSIsContainer};Descending=$true}, Name
foreach ($item in $items) {
if ($item.PSIsContainer) {
$childCluster = Allocate-Chain $clusterSize
Add-DirectoryEntry $entries $item.Name 0x10 $childCluster 0 $used
Write-Directory $Stream $item.FullName $childCluster $SelfCluster $false
} else {
$fileSize = [uint32]$item.Length
$fileCluster = Allocate-Chain $item.Length
Add-DirectoryEntry $entries $item.Name 0x20 $fileCluster $fileSize $used
Write-FileContent $Stream $item.FullName $fileCluster
}
}
$entryBytes = $entries.Count * 32
$neededBytes = [Math]::Max($clusterSize, [int](($entryBytes + $clusterSize - 1) / $clusterSize) * $clusterSize)
$dirBytes = [byte[]]::new($neededBytes)
$offset = 0
foreach ($entry in $entries) {
[Array]::Copy($entry, 0, $dirBytes, $offset, 32)
$offset += 32
}
Write-At $Stream (Get-ClusterOffset $SelfCluster) $dirBytes
}
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $ImagePath) | Out-Null
$fs = [System.IO.File]::Open($ImagePath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
try {
$fs.SetLength($SizeMB * 1024 * 1024)
$mbr = [byte[]]::new(512)
$mbr[446 + 4] = 0xee
Set-Le32 $mbr (446 + 8) 1
$protectiveSectors = [UInt64]($totalSectors - 1)
if ($protectiveSectors -gt [UInt64]4294967295) { $protectiveSectors = [UInt64]4294967295 }
Set-Le32 $mbr (446 + 12) $protectiveSectors
$mbr[510] = 0x55
$mbr[511] = 0xaa
Write-At $fs 0 $mbr
$partitionEntries = [byte[]]::new($entryCount * $entrySize)
$espType = Get-GuidBytes 'c12a7328-f81f-11d2-ba4b-00a0c93ec93b'
$partGuid = ([Guid]::NewGuid()).ToByteArray()
[Array]::Copy($espType, 0, $partitionEntries, 0, 16)
[Array]::Copy($partGuid, 0, $partitionEntries, 16, 16)
Set-Le64 $partitionEntries 32 $partStart
Set-Le64 $partitionEntries 40 $partEnd
$nameBytes = [System.Text.Encoding]::Unicode.GetBytes('Deshab ESP')
[Array]::Copy($nameBytes, 0, $partitionEntries, 56, $nameBytes.Length)
$entriesCrc = Get-Crc32 $partitionEntries
$diskGuid = ([Guid]::NewGuid()).ToByteArray()
$primaryHeader = New-GptHeader 1 $lastLba $firstUsable $lastUsable $diskGuid 2 $entryCount $entrySize $entriesCrc
$backupEntriesLba = [UInt64]($lastLba - $entrySectors)
$backupHeader = New-GptHeader $lastLba 1 $firstUsable $lastUsable $diskGuid $backupEntriesLba $entryCount $entrySize $entriesCrc
Write-At $fs ($bytesPerSector * 1) $primaryHeader
Write-At $fs ($bytesPerSector * 2) $partitionEntries
Write-At $fs ([Int64]($backupEntriesLba * $bytesPerSector)) $partitionEntries
Write-At $fs ([Int64]($lastLba * $bytesPerSector)) $backupHeader
$boot = [byte[]]::new(512)
$boot[0] = 0xeb; $boot[1] = 0x58; $boot[2] = 0x90
[Array]::Copy([System.Text.Encoding]::ASCII.GetBytes('DESHAB '), 0, $boot, 3, 8)
Set-Le16 $boot 11 $bytesPerSector
$boot[13] = [byte]$sectorsPerCluster
Set-Le16 $boot 14 $reservedSectors
$boot[16] = [byte]$numFats
Set-Le16 $boot 17 0
Set-Le16 $boot 19 0
$boot[21] = 0xf8
Set-Le16 $boot 22 0
Set-Le16 $boot 24 63
Set-Le16 $boot 26 255
Set-Le32 $boot 28 $partStart
Set-Le32 $boot 32 $partSectors
Set-Le32 $boot 36 $fatSectors
Set-Le16 $boot 40 0
Set-Le16 $boot 42 0
Set-Le32 $boot 44 2
Set-Le16 $boot 48 1
Set-Le16 $boot 50 6
$boot[64] = 0x80
$boot[66] = 0x29
Set-Le32 $boot 67 0x44534842
[Array]::Copy([System.Text.Encoding]::ASCII.GetBytes('DESHAB '), 0, $boot, 71, 11)
[Array]::Copy([System.Text.Encoding]::ASCII.GetBytes('FAT32 '), 0, $boot, 82, 8)
$boot[510] = 0x55; $boot[511] = 0xaa
Write-At $fs $partitionOffset $boot
Write-At $fs ($partitionOffset + 6 * $bytesPerSector) $boot
$fsinfo = [byte[]]::new(512)
Set-Le32 $fsinfo 0 0x41615252
Set-Le32 $fsinfo 484 0x61417272
Set-Le32 $fsinfo 488 4294967295
Set-Le32 $fsinfo 492 3
$fsinfo[508] = 0x00; $fsinfo[509] = 0x00; $fsinfo[510] = 0x55; $fsinfo[511] = 0xaa
Write-At $fs ($partitionOffset + $bytesPerSector) $fsinfo
Write-At $fs ($partitionOffset + 7 * $bytesPerSector) $fsinfo
Write-Directory $fs $SourceDir 2 2 $true
$fatBytes = [byte[]]::new($fatSectors * $bytesPerSector)
for ($i = 0; $i -lt $fat.Length; $i++) {
$off = $i * 4
if ($off + 3 -ge $fatBytes.Length) { break }
Set-Le32 $fatBytes $off $fat[$i]
}
Write-At $fs $fatOffset $fatBytes
Write-At $fs ($fatOffset + $fatSectors * $bytesPerSector) $fatBytes
}
finally {
$fs.Close()
}
Write-Host "[build] GPT + FAT32 ESP IMG: $ImagePath"
}
Write-Host '[build] Building UTSM kernel...'
$clang = Find-Tool 'clang'
$lld = Find-Tool 'ld.lld'
$make = Find-Tool 'make' @('mingw32-make')
if (-not $clang) {
throw 'clang was not found. Install LLVM/Clang or provide a freestanding x86_64 ELF toolchain.'
}
if (-not $lld) {
throw 'ld.lld was not found. Install LLVM lld or provide a compatible linker.'
}
if (-not $make) {
throw 'make was not found. Install make.'
}
Write-Host "[build] clang: $clang"
Write-Host "[build] ld.lld: $lld"
Write-Host "[build] make: $make"
New-Item -ItemType Directory -Force -Path $BuildTmp | Out-Null
$env:TMP = $BuildTmp
$env:TEMP = $BuildTmp
Push-Location $UtsmDir
try {
& $make -f MAKEFILE "CC=$clang" "LD=$lld"
if ($LASTEXITCODE -ne 0) {
throw "make UTSM failed with exit code $LASTEXITCODE"
}
}
finally {
Pop-Location
}
Write-Host '[build] Building DSK main kernel...'
2026-07-31 13:53:49 +08:00
# Generate logo_data.c from Logo.png (RGBA8888 embedded data)
$LogoPng = Join-Path $Root 'Logo.png'
$LogoDataC = Join-Path $DskDir 'logo_data.c'
$PngToRgba = Join-Path $DskDir 'png_to_rgba.py'
if (Test-Path $LogoPng) {
# Find Python
$Python = $null
foreach ($p in @('C:\Users\林濬哲\AppData\Local\Programs\Python\Python312\python.exe',
'C:\msys64\mingw64\bin\python.exe',
'python', 'python3')) {
try { $cmd = Get-Command $p -ErrorAction Stop; $Python = $cmd.Source; break } catch {}
}
if ($Python) {
# Regenerate if Logo.png is newer than logo_data.c
if (-not (Test-Path $LogoDataC) -or ((Get-Item $LogoPng).LastWriteTime -gt (Get-Item $LogoDataC).LastWriteTime)) {
Write-Host '[build] Generating logo_data.c from Logo.png...'
& $Python $PngToRgba $LogoPng $LogoDataC
if ($LASTEXITCODE -ne 0) { Write-Host '[build] WARNING: logo_data.c generation failed, using existing file' }
}
} else {
Write-Host '[build] WARNING: Python not found, cannot regenerate logo_data.c'
}
} else {
Write-Host '[build] WARNING: Logo.png not found'
}
try {
& $make -C "$DskDir" -f MAKEFILE "CC=$clang" "LD=$lld"
if ($LASTEXITCODE -ne 0) {
throw "make DSK failed with exit code $LASTEXITCODE"
}
}
finally {
}
if (-not (Test-Path $Output)) {
throw "UTSM build did not produce $Output"
}
if (-not (Test-Path $DskOutput)) {
throw "DSK build did not produce $DskOutput"
}
$FirstInitDir = Join-Path $Root 'CODE\firstInit'
$FirstInitOutput = Join-Path $SystemDir 'system\user\use\FirstInit.elf'
Write-Host '[build] Building FirstInit.elf...'
try {
& $make -C "$FirstInitDir" -f MAKEFILE "CC=$clang" "LD=$lld"
if ($LASTEXITCODE -ne 0) {
throw "make FirstInit failed with exit code $LASTEXITCODE"
}
}
finally {
}
if (-not (Test-Path $FirstInitOutput)) {
throw "FirstInit build did not produce $FirstInitOutput"
}
Write-Host "[build] FirstInit Output: $FirstInitOutput"
$LoginDir = Join-Path $Root 'CODE\login'
$LoginOutput = Join-Path $SystemDir 'system\user\use\login.elf'
Write-Host '[build] Building login.elf...'
try {
& $make -C "$LoginDir" -f MAKEFILE "CC=$clang" "LD=$lld"
if ($LASTEXITCODE -ne 0) {
throw "make login failed with exit code $LASTEXITCODE"
}
}
finally {
}
if (-not (Test-Path $LoginOutput)) {
throw "login build did not produce $LoginOutput"
}
Write-Host "[build] login Output: $LoginOutput"
$MouseDir = Join-Path $Root 'CODE\mouse'
$MouseOutput = Join-Path $SystemDir 'system\deshab64\mouse\mouseInit.elf'
Write-Host '[build] Building mouseInit.elf...'
try {
& $make -C "$MouseDir" -f MAKEFILE "CC=$clang" "LD=$lld"
if ($LASTEXITCODE -ne 0) {
throw "make mouseInit failed with exit code $LASTEXITCODE"
}
}
finally {
}
if (-not (Test-Path $MouseOutput)) {
throw "mouseInit build did not produce $MouseOutput"
}
Write-Host "[build] mouseInit Output: $MouseOutput"
$NetmanDir = Join-Path $Root 'CODE\netman'
$NetmanOutput = Join-Path $SystemDir 'system\deshab64\network\netman.elf'
Write-Host '[build] Building netman.elf...'
try {
& $make -C "$NetmanDir" -f MAKEFILE "CC=$clang" "LD=$lld"
if ($LASTEXITCODE -ne 0) {
throw "make netman failed with exit code $LASTEXITCODE"
}
}
finally {
}
if (-not (Test-Path $NetmanOutput)) {
throw "netman build did not produce $NetmanOutput"
}
Write-Host "[build] netman Output: $NetmanOutput"
$ShellDir = Join-Path $Root 'CODE\shell'
$ShellOutput = Join-Path $SystemDir 'system\deshab64\shell.elf'
Write-Host '[build] Building shell.elf...'
try {
& $make -C "$ShellDir" -f MAKEFILE "CC=$clang" "LD=$lld"
if ($LASTEXITCODE -ne 0) {
throw "make shell failed with exit code $LASTEXITCODE"
}
}
finally {
}
if (-not (Test-Path $ShellOutput)) {
throw "shell build did not produce $ShellOutput"
}
Write-Host "[build] shell Output: $ShellOutput"
$DesktopDir = Join-Path $Root 'CODE\desktop'
$DesktopOutput = Join-Path $SystemDir 'system\deshab64\desktop.elf'
Write-Host '[build] Building desktop.elf...'
try {
& $make -C "$DesktopDir" -f MAKEFILE "CC=$clang" "LD=$lld"
if ($LASTEXITCODE -ne 0) {
throw "make desktop failed with exit code $LASTEXITCODE"
}
}
finally {
}
if (-not (Test-Path $DesktopOutput)) {
throw "desktop build did not produce $DesktopOutput"
}
Write-Host "[build] desktop Output: $DesktopOutput"
2026-07-28 14:35:14 +08:00
# ---------- CMD (Windows-style command line + PE/EXE compat) ----------
$CmdDir = Join-Path $Root 'CODE\cmd'
$CmdOutput = Join-Path $SystemDir 'system\deshab64\cmd.elf'
Write-Host '[build] Building cmd.elf...'
try {
& $make -C "$CmdDir" -f MAKEFILE "CC=$clang" "LD=$lld"
if ($LASTEXITCODE -ne 0) {
throw "make cmd failed with exit code $LASTEXITCODE"
}
}
finally {
}
if (-not (Test-Path $CmdOutput)) {
throw "cmd build did not produce $CmdOutput"
}
Write-Host "[build] cmd Output: $CmdOutput"
# ---------- Tool applications ----------
$ToolsDir = Join-Path $Root 'CODE\tools'
$ToolsOutDir = Join-Path $SystemDir 'system\deshab64\tools'
$toolApps = @('editor', 'fileman', 'browser', 'curl', 'ping')
foreach ($tool in $toolApps) {
$toolDir = Join-Path $ToolsDir $tool
$toolMake = Join-Path $toolDir 'MAKEFILE'
if (Test-Path $toolMake) {
Write-Host "[build] Building ${tool}.elf..."
try {
& $make -C "$toolDir" -f MAKEFILE "CC=$clang" "LD=$lld"
if ($LASTEXITCODE -ne 0) {
Write-Host "[build] WARNING: make $tool failed with exit code $LASTEXITCODE"
}
}
finally {
}
}
}
Write-Host '[build] Building DKM framebuffer driver...'
Build-DkmDriver (Join-Path $Root 'CODE\DKM\console_fb\console_fb.c') (Join-Path $SystemDir 'driver\console\console_fb.drv')
# B7: apic.drv 纳入构建 (原 platform/apic.drv 为预置二进制, 不随 apic.c 更新)
Write-Host '[build] Building DKM platform apic driver...'
Build-DkmDriver (Join-Path $Root 'CODE\DKM\apic\apic.c') (Join-Path $SystemDir 'driver\platform\apic.drv')
2026-07-31 13:53:49 +08:00
Write-Host '[build] Building DKM storage drivers...'
Build-DkmDriver (Join-Path $Root 'CODE\DKM\nvme\nvme.c') (Join-Path $SystemDir 'driver\block\nvme.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\xhci\xhci.c') (Join-Path $SystemDir 'driver\block\xhci.drv')
Write-Host '[build] Building DKM network drivers...'
Build-DkmDriver (Join-Path $Root 'CODE\DKM\e1000\e1000.c') (Join-Path $SystemDir 'driver\net\e1000.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\virtio_net\virtio_net.c') (Join-Path $SystemDir 'driver\net\virtio_net.drv')
Build-DkmDriver (Join-Path $Root 'CODE\DKM\ath9k\ath9k.c') (Join-Path $SystemDir 'driver\net\ath9k.drv')
Write-Host "[build] Output: $Output"
Write-Host "[build] DSK Output: $DskOutput"
Write-Host '[build] Packaging SYSTEM to GPT + FAT32 IMG...'
2026-07-31 13:53:49 +08:00
New-GptFat32Image $SystemDir $ImagePath 768
Write-Host "[build] Done: $ImagePath"
# Rebuild SATA FAT32 disk image (DSK reads deshab.elf/FirstInit.elf/mouseInit.elf from here)
Write-Host '[build] Rebuilding SATA FAT32 disk image...'
$BuildTmp = Join-Path $Root '.build_tmp'
$MkFat32Ps = Join-Path $BuildTmp 'mkfat32.ps1'
if (Test-Path $MkFat32Ps) {
& powershell -ExecutionPolicy Bypass -File $MkFat32Ps
if ($LASTEXITCODE -ne 0) {
Write-Host "[build] WARNING: mkfat32.ps1 exited with $LASTEXITCODE"
}
Write-Host "[build] SATA IMG: $(Join-Path $BuildTmp 'sata_fat32_dsk.img')"
} else {
Write-Host '[build] WARNING: mkfat32.ps1 not found, SATA image not rebuilt'
}
2026-07-31 13:53:49 +08:00
# NVMe 测试盘镜像 (64MB, LBA0 带 DESHABNVME0 签名, 用于验证 NVMe block 数据路径)
$NvmeImg = Join-Path $BuildTmp 'nvme_test.img'
if (-not (Test-Path $NvmeImg)) {
Write-Host '[build] Creating NVMe test disk image...'
$nfs = [System.IO.File]::Open($NvmeImg, [System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
try {
$nfs.SetLength(64MB)
$sigSector = [byte[]]::new(512)
$sigText = [System.Text.Encoding]::ASCII.GetBytes('DESHABNVME0')
for ($i = 0; $i -lt 512; $i++) { $sigSector[$i] = $sigText[$i % $sigText.Length] }
Write-At $nfs 0 $sigSector
}
finally {
$nfs.Close()
}
Write-Host "[build] NVMe IMG: $NvmeImg"
}
# USB xHCI test disk image (64MB, for USB Mass Storage driver testing)
$UsbImg = Join-Path $BuildTmp 'usb_test.img'
if (-not (Test-Path $UsbImg)) {
Write-Host '[build] Creating USB test disk image...'
$ufs = [System.IO.File]::Open($UsbImg, [System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
try {
$ufs.SetLength(64MB)
$sigSector = [byte[]]::new(512)
$sigText = [System.Text.Encoding]::ASCII.GetBytes('DESHABUSB00')
for ($i = 0; $i -lt 512; $i++) { $sigSector[$i] = $sigText[$i % $sigText.Length] }
Write-At $ufs 0 $sigSector
}
finally {
$ufs.Close()
}
Write-Host "[build] USB IMG: $UsbImg"
}