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:
2026-07-19 13:34:32 +01:00
parent 59034be9e6
commit 1ec33767ab
104 changed files with 7111 additions and 257 deletions
+237
View File
@@ -0,0 +1,237 @@
import os
import shutil
from command import Command
from config import Config
from cache import Cache
from seed import Seed
from target import Target
from env import Environment
from platform import Platform
from colorama import Fore, Style
class SetCommand(Command):
def __init__(self):
super().__init__(
name='set',
description='Set a configuration key value.')
def run(self, args):
key = args.get_positional(0)
value = args.get_positional(1)
if key is None or value is None:
print('usage: meadow set <key> <value>')
return -1
try:
value_i = int(value)
Config.set(key, value_i)
except:
Config.set(key, value)
return 0
class SeedsCommand(Command):
def __init__(self):
super().__init__(
name='seeds',
description='List all available seeds.')
def write_dot(self, path):
f = open(path, mode='w')
f.write('digraph seeds {\n')
f.write(' node [shape=Mrecord];\n')
nodes = {}
seeds = Seed.all_seeds()
targets = Target.all_targets()
for s in seeds.values():
if s.name() in nodes:
continue
sname = s.name().replace('-', '_')
nodes[s.name()] = s
f.write(' {} [label="{}"];\n'.format(sname, s.name()))
for d in s.dependencies().values():
continue
for t in targets.values():
if t.name() in nodes:
continue
t.resolve()
tname = t.name().replace('-', '_')
nodes[t.name()] = t
f.write(' {} [label="{}",shape=hexagon];\n'.format(tname, t.name()))
for s in seeds.values():
for k, d in s.dependencies().items():
if d.name() not in nodes:
nodes[d.name()] = d
dname = d.name().replace('-', '_')
f.write(' {} [label="{}",shape=diamond];\n'.format(dname, d.name()))
sname = s.name().replace('-', '_')
dname = d.name().replace('-', '_')
if d.is_resolved():
f.write(' {} -> {};\n'.format(dname, sname))
else:
f.write(' {} -> {}[style=dotted];\n'.format(dname, sname))
for t in targets.values():
for k, d in t.dependencies().items():
tname = t.name().replace('-', '_')
dname = d.name().replace('-', '_')
if d.is_resolved():
f.write(' {} -> {};\n'.format(dname, tname))
else:
f.write(' {} -> {}[style=dotted];\n'.format(dname, tname))
f.write('}\n')
f.close()
def run(self, args):
seeds = Seed.all_seeds()
dot_path = args.get_named('to-dot-file', 0, 1)
if dot_path is not None:
self.write_dot(dot_path)
return 0
for s in seeds.values():
print('{}'.format(s.name()))
class PlatformsCommand(Command):
def __init__(self):
super().__init__(
name='platforms',
description='List all available platforms')
def run(self, args):
print('Supported platforms:')
platforms = Platform.all_platforms()
current_platform = Platform.current_platform()
for p in platforms.values():
if current_platform is not None and p.name() == current_platform.name():
print(f'* {Fore.GREEN}{p.name()}{Style.RESET_ALL}')
else:
print(f' {p.name()}')
class HelpCommand(Command):
def __init__(self):
super().__init__(
name='help',
description='List all available commands.')
def run(self, args):
print('Available commands:')
commands = Command.all_commands()
max_len = 0
for c in commands.values():
if len(c.name()) > max_len:
max_len = len(c.name())
max_len += 5
for c in commands.values():
print(' ', end='')
print(c.name(), end='')
for i in range(len(c.name()), max_len):
print(' ', end='')
print(c.description())
class BuildCommand(Command):
def __init__(self):
super().__init__(
name='build',
description='Build the currently selected target.')
def run(self, args):
verbose = args.get_named('verbose', 0, 0)
current_target = Target.current_target()
if current_target is None:
print(f'{Fore.RED}Err:{Style.RESET_ALL} no build target has been set. Use `meadow set target.name <...>` to specify which target to build')
return -1
current_platform = Platform.current_platform()
if current_platform is None:
print(f'{Fore.RED}Err:{Style.RESET_ALL} no target platform has been set. Use `meadow set target.platform <...>` to specify which platform to target')
return -1
components = current_target.resolve()
for c in components:
build_system = None
if isinstance(c, Seed):
build_system = c.build_system()
build_system.check_out_of_date()
for c in components:
build_system = None
if isinstance(c, Seed):
build_system = c.build_system()
if build_system is not None:
out_of_date = \
build_system.is_configuration_out_of_date() \
or build_system.is_build_out_of_date()
if out_of_date:
print(f'{Fore.GREEN}>>>{Style.RESET_ALL} Building {c.name()}')
if build_system.is_configuration_out_of_date():
print(f'{Fore.CYAN} >>{Style.RESET_ALL} Configuring build...')
build_system.configure(verbose=verbose)
if out_of_date:
print(f'{Fore.CYAN} >>{Style.RESET_ALL} Building...')
build_system.build(verbose=verbose)
print(f'{Fore.CYAN} >>{Style.RESET_ALL} Installing...')
build_system.install(verbose=verbose)
print(f'{Style.BRIGHT}{Fore.GREEN}>>> Build Complete!{Style.RESET_ALL}')
class CleanCommand(Command):
def __init__(self):
super().__init__(
name='clean',
description='Delete all build-related files.')
def run(self, args):
build_dir = Environment.build_root()
for i in os.listdir(build_dir):
if i.startswith('meadow') and ('cache' not in i):
continue
ipath = os.path.join(build_dir, i)
if os.path.isdir(ipath):
shutil.rmtree(ipath)
else:
os.remove(ipath)
Cache.reset()
return 0