meta: replace cmake mono-build system with a custom build co-ordinator
every system component now has its own self-contained build system, and a seed file describing the component and its build system and dependencies. a new tool, meadow, collects and uses these seed files to build the components into a full system. meadow also manages the target system root and a host prefix where toolchain tools are installed to. the build system has also been updated to use a new $ARCH-rosetta-gcc compiler, and the patches files necessary to build it have been added to toolchain/cross-compiler.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
class Args:
|
||||
def __init__(self, argv):
|
||||
self.__argv = argv
|
||||
|
||||
|
||||
def get_positional(self, index):
|
||||
if len(self.__argv) > index:
|
||||
return self.__argv[index]
|
||||
return None
|
||||
|
||||
|
||||
def get_named(self, name, index, nr_args):
|
||||
found_name = False
|
||||
values = []
|
||||
|
||||
for s in self.__argv:
|
||||
if found_name:
|
||||
if len(values) < nr_args:
|
||||
values.append(s)
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
if s == '--{}'.format(name):
|
||||
if index > 0:
|
||||
index -= 1
|
||||
continue
|
||||
|
||||
found_name = True
|
||||
continue
|
||||
|
||||
if nr_args == 0:
|
||||
return found_name
|
||||
|
||||
if len(values) < nr_args or not found_name:
|
||||
return None
|
||||
|
||||
if len(values) == 1:
|
||||
return values[0]
|
||||
|
||||
return values
|
||||
@@ -0,0 +1,98 @@
|
||||
import os
|
||||
import struct
|
||||
import tarfile
|
||||
from pathlib import Path
|
||||
from elf import ELF64Image
|
||||
|
||||
|
||||
class BSPWriteError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BSP:
|
||||
def __init__(self, out_path, **kwargs):
|
||||
if os.path.exists(out_path):
|
||||
os.remove(out_path)
|
||||
|
||||
self.__bootstrap_exec_path = None
|
||||
self.__path = out_path
|
||||
self.__sysroot = kwargs.get('sysroot', None)
|
||||
|
||||
out_dir = os.path.dirname(out_path)
|
||||
Path(out_dir).mkdir(parents=True, exist_ok=True)
|
||||
self.__tarfile = tarfile.open(name=out_path, mode='x')
|
||||
|
||||
|
||||
def add_file(self, src, dst):
|
||||
if self.__sysroot is not None:
|
||||
while src[0] == '/':
|
||||
src = src[1:]
|
||||
src = os.path.join(self.__sysroot, src)
|
||||
print(f'tarfile.add({src}, arcname={dst})')
|
||||
self.__tarfile.add(src, arcname=dst)
|
||||
|
||||
|
||||
def set_bootstrap_program(self, path):
|
||||
if self.__sysroot is not None:
|
||||
while path[0] == '/':
|
||||
path = path[1:]
|
||||
path = os.path.join(self.__sysroot, path)
|
||||
self.__bootstrap_exec_path = path
|
||||
|
||||
|
||||
def __write_trailer(self):
|
||||
bootstrap_offset = os.path.getsize(self.__path)
|
||||
print(f'bootstrap_offset was {bootstrap_offset}')
|
||||
bsp = open(self.__path, mode='ab')
|
||||
prog = open(self.__bootstrap_exec_path, mode='rb')
|
||||
|
||||
padding = 0x1000 - (bootstrap_offset % 0x1000)
|
||||
bsp.write(b'\0' * padding)
|
||||
bootstrap_offset += padding
|
||||
prog_len = 0
|
||||
print(f'bootstrap_offset is now {bootstrap_offset}')
|
||||
|
||||
while True:
|
||||
data = prog.read(1024)
|
||||
bsp.write(data)
|
||||
prog_len += len(data)
|
||||
|
||||
if len(data) < 1024:
|
||||
break
|
||||
|
||||
prog.close()
|
||||
|
||||
prog_elf = ELF64Image()
|
||||
if prog_elf.load(self.__bootstrap_exec_path) != 0:
|
||||
raise BSPWriteError(f'{self.__bootstrap_exec_path} is not a valid ELF binary')
|
||||
|
||||
for ph in prog_elf.ph_list:
|
||||
print('{}: {}/{} ({}/{} bytes) {}'.format(
|
||||
ph.type,
|
||||
ph.offset, ph.vaddr,
|
||||
ph.filesz, ph.memsz, ph.flags))
|
||||
|
||||
trailer = struct.pack('>IQIQIQQQQQQQ',
|
||||
0xcafebabe,
|
||||
0, bootstrap_offset,
|
||||
bootstrap_offset, prog_len,
|
||||
prog_elf.text.offset, prog_elf.text.vaddr, prog_elf.text.memsz,
|
||||
prog_elf.data.offset, prog_elf.data.vaddr, prog_elf.data.memsz,
|
||||
prog_elf.entry)
|
||||
|
||||
bsp.write(trailer)
|
||||
|
||||
bsp.close()
|
||||
prog.close()
|
||||
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
|
||||
def close(self):
|
||||
self.__tarfile.close()
|
||||
if self.__bootstrap_exec_path is None:
|
||||
return
|
||||
|
||||
self.__write_trailer()
|
||||
@@ -0,0 +1,102 @@
|
||||
from enum import Enum, Flag
|
||||
import struct
|
||||
|
||||
class ELFPType(Enum):
|
||||
NULL = 0
|
||||
LOAD = 1
|
||||
DYNAMIC = 2
|
||||
INTERP = 3
|
||||
NOTE = 4
|
||||
SHLIB = 5
|
||||
PHDR = 6
|
||||
TLS = 7
|
||||
|
||||
|
||||
class ELFPFlag(Flag):
|
||||
NONE = 0
|
||||
EXEC = 1
|
||||
WRITE = 2
|
||||
READ = 4
|
||||
|
||||
|
||||
class ELFEndian(Enum):
|
||||
LITTLE = 1
|
||||
BIG = 2
|
||||
|
||||
|
||||
class ELF64Phdr:
|
||||
def __init__(self):
|
||||
self.type = ELFPType.NULL
|
||||
self.flags = 0
|
||||
self.offset = 0
|
||||
self.vaddr = 0
|
||||
self.paddr = 0
|
||||
self.filesz = 0
|
||||
self.memsz = 0
|
||||
self.align = 0
|
||||
|
||||
return
|
||||
|
||||
def parse(self, buf, endian):
|
||||
endian_marker = '>' if endian == ELFEndian.BIG else '<'
|
||||
entry = struct.unpack('{}IIQQQQQQ'.format(endian_marker), buf)
|
||||
|
||||
self.type = ELFPType(entry[0])
|
||||
self.flags = ELFPFlag.NONE
|
||||
self.offset = entry[2]
|
||||
self.vaddr = entry[3]
|
||||
self.paddr = entry[4]
|
||||
self.filesz = entry[5]
|
||||
self.memsz = entry[6]
|
||||
self.align = entry[7]
|
||||
|
||||
if entry[1] & ELFPFlag.READ.value: self.flags |= ELFPFlag.READ
|
||||
if entry[1] & ELFPFlag.WRITE.value: self.flags |= ELFPFlag.WRITE
|
||||
if entry[1] & ELFPFlag.EXEC.value: self.flags |= ELFPFlag.EXEC
|
||||
|
||||
|
||||
class ELF64Image:
|
||||
def __init__(self):
|
||||
self.ph_list = []
|
||||
self.text = None
|
||||
self.data = None
|
||||
self.entry = None
|
||||
|
||||
def load(self, path):
|
||||
f = open(path, mode='rb')
|
||||
ident_bytes = f.read(16)
|
||||
|
||||
ident = struct.unpack('BBBBBBBBBBBBBBBB', ident_bytes)
|
||||
|
||||
if ident[0] != 0x7F or ident[1] != ord('E') or ident[2] != ord('L') or ident[3] != ord('F'):
|
||||
f.close()
|
||||
return -1
|
||||
|
||||
endian = ELFEndian(ident[5])
|
||||
endian_marker = '>' if endian == ELFEndian.BIG else '<'
|
||||
|
||||
hdr_bytes = f.read(48)
|
||||
hdr = struct.unpack('{}HHIQQQIHHHHHH'.format(endian_marker), hdr_bytes)
|
||||
|
||||
self.entry = hdr[3]
|
||||
phoff = hdr[4]
|
||||
phnum = hdr[9]
|
||||
phentsize = hdr[8]
|
||||
|
||||
for i in range(0, phnum):
|
||||
f.seek(phoff + (i * phentsize))
|
||||
phdr_bytes = f.read(phentsize)
|
||||
|
||||
phdr = ELF64Phdr()
|
||||
phdr.parse(phdr_bytes, endian)
|
||||
|
||||
if phdr.flags == ELFPFlag.READ | ELFPFlag.EXEC:
|
||||
self.text = phdr
|
||||
if phdr.flags == ELFPFlag.READ | ELFPFlag.WRITE:
|
||||
self.data = phdr
|
||||
|
||||
self.ph_list.append(phdr)
|
||||
|
||||
f.close()
|
||||
return 0
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import sys
|
||||
import os
|
||||
import tomli
|
||||
from colorama import init as colorama_init
|
||||
from colorama import Fore, Style
|
||||
from args import Args
|
||||
from preset import Preset
|
||||
from bsp import BSP
|
||||
|
||||
|
||||
def main():
|
||||
colorama_init()
|
||||
|
||||
args = Args(sys.argv)
|
||||
|
||||
preset_path = args.get_named('preset', 0, 1)
|
||||
if preset_path is None:
|
||||
print(f'{Fore.RED}Err:{Style.RESET_ALL} no preset specified')
|
||||
return -1
|
||||
|
||||
out_path = args.get_named('out', 0, 1)
|
||||
if out_path is None:
|
||||
print(f'{Fore.RED}Err:{Style.RESET_ALL} no output path specified')
|
||||
return -1
|
||||
|
||||
sysroot_path = args.get_named('sysroot', 0, 1)
|
||||
if sysroot_path is None:
|
||||
sysroot_path = '/'
|
||||
|
||||
preset = Preset(preset_path)
|
||||
|
||||
bsp = BSP(out_path, sysroot=sysroot_path)
|
||||
for dst, src in preset.contents().items():
|
||||
for src_filepath in src:
|
||||
src_filename = os.path.basename(src_filepath)
|
||||
dst_filepath = os.path.join(dst, src_filename)
|
||||
bsp.add_file(src_filepath, dst_filepath)
|
||||
|
||||
bsp.set_bootstrap_program(preset.bootstrap_program())
|
||||
bsp.close()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
SOURCE_ROOT="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/../.."
|
||||
|
||||
if [ ! -d "$SOURCE_ROOT/build" ]; then
|
||||
mkdir build
|
||||
fi
|
||||
|
||||
if [ ! -d "$SOURCE_ROOT/build/mkbsp_venv" ]; then
|
||||
echo 'Initialising mkbsp...'
|
||||
python3 -m venv $SOURCE_ROOT/build/mkbsp_venv
|
||||
source $SOURCE_ROOT/build/mkbsp_venv/bin/activate
|
||||
pip install -r $SOURCE_ROOT/toolchain/mkbsp/requirements.txt
|
||||
else
|
||||
source $SOURCE_ROOT/build/mkbsp_venv/bin/activate
|
||||
fi
|
||||
|
||||
python3 $SOURCE_ROOT/toolchain/mkbsp/main.py "$@"
|
||||
@@ -0,0 +1,66 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import tomli
|
||||
|
||||
|
||||
class PresetParseError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Preset:
|
||||
def __collect_preset_files(self):
|
||||
result = []
|
||||
for root, dirs, files in os.walk(self.__path):
|
||||
for f in files:
|
||||
name = f
|
||||
path = os.path.join(root, f)
|
||||
|
||||
result.append((name, path))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def __apply_preset_file(self, data):
|
||||
bootstrap = data.get('bootstrap', None)
|
||||
contents = data.get('contents', None)
|
||||
if bootstrap is not None:
|
||||
self.__bootstrap = bootstrap
|
||||
|
||||
if contents is not None:
|
||||
for dest, src in contents.items():
|
||||
dest_list = self.__contents.get(dest, [])
|
||||
|
||||
for p in src:
|
||||
dest_list.append(p)
|
||||
|
||||
self.__contents[dest] = dest_list
|
||||
|
||||
|
||||
def __read_preset_files(self):
|
||||
for n, p in self.__files:
|
||||
data = None
|
||||
try:
|
||||
f = open(p, mode='rb')
|
||||
data = tomli.load(f)
|
||||
except Exception as e:
|
||||
raise ParseError('Err: Failed to parse seed file {}: {}'.format(seed_path, e))
|
||||
|
||||
self.__apply_preset_file(data)
|
||||
|
||||
|
||||
def __init__(self, path):
|
||||
self.__bootstrap = {}
|
||||
self.__contents = {}
|
||||
self.__path = path
|
||||
self.__files = self.__collect_preset_files()
|
||||
self.__files = sorted(self.__files, key=lambda tup: tup[0])
|
||||
self.__read_preset_files()
|
||||
|
||||
|
||||
def contents(self):
|
||||
return self.__contents
|
||||
|
||||
|
||||
def bootstrap_program(self):
|
||||
return self.__bootstrap.get('bootstrap_exec', None)
|
||||
@@ -0,0 +1,2 @@
|
||||
tomli
|
||||
colorama
|
||||
Reference in New Issue
Block a user