diff --git a/build/ab.mk b/build/ab.mk index 5674b1a9..024cefa9 100644 --- a/build/ab.mk +++ b/build/ab.mk @@ -50,6 +50,5 @@ build-files = $(shell find . -name 'build.py') $(wildcard build/*.py) $(wildcard $(OBJ)/build.mk: Makefile $(build-files) @echo "AB" @mkdir -p $(OBJ) - $(hide) $(PYTHON) -X pycache_prefix=$(OBJ) build/ab.py $(patsubst %,-t %,$(TARGETS)) -o $@ \ - build.py || rm -f $@ - + $(hide) $(PYTHON) -X pycache_prefix=$(OBJ) build/ab.py -o $@ build.py \ + || rm -f $@ diff --git a/build/ab.py b/build/ab.py index 025b33a0..4865bb87 100644 --- a/build/ab.py +++ b/build/ab.py @@ -1,136 +1,81 @@ -from collections.abc import Iterable, Sequence from os.path import * -from types import SimpleNamespace +from pathlib import Path +from typing import Iterable import argparse +import builtins +from copy import copy import functools import importlib import importlib.abc import importlib.util +from importlib.machinery import ( + SourceFileLoader, + PathFinder, + ModuleSpec, +) import inspect -import re -import sys -import builtins import string -import fnmatch -import traceback +import sys -defaultGlobals = {} +verbose = False +quiet = False +cwdStack = [""] targets = {} -unmaterialisedTargets = set() +unmaterialisedTargets = {} # dict, not set, to get consistent ordering materialisingStack = [] -outputFp = None -cwdStack = [""] +defaultGlobals = {} sys.path += ["."] old_import = builtins.__import__ -def new_import(name, *args, **kwargs): - if name not in sys.modules: - path = name.replace(".", "/") + ".py" - if isfile(path): - sys.stderr.write(f"loading {path}\n") - loader = importlib.machinery.SourceFileLoader(name, path) - - spec = importlib.util.spec_from_loader( - name, loader, origin="built-in" - ) - module = importlib.util.module_from_spec(spec) - sys.modules[name] = module - cwdStack.append(dirname(path)) - spec.loader.exec_module(module) - cwdStack.pop() - - return old_import(name, *args, **kwargs) +class PathFinderImpl(PathFinder): + def find_spec(self, fullname, path, target=None): + if not path: + path = ["."] + if len(path) != 1: + return None + try: + path = relpath(path[0]) + except ValueError: + return None -builtins.__import__ = new_import + realpath = fullname.replace(".", "/") + buildpath = realpath + ".py" + if isfile(buildpath): + spec = importlib.util.spec_from_file_location( + name=fullname, + location=buildpath, + loader=BuildFileLoaderImpl(fullname=fullname, path=buildpath), + submodule_search_locations=[], + ) + return spec + if isdir(realpath): + return ModuleSpec(fullname, None, origin=realpath, is_package=True) + return None -class ABException(BaseException): - pass +class BuildFileLoaderImpl(SourceFileLoader): + def exec_module(self, module): + sourcepath = relpath(module.__file__) + if not quiet: + print("loading", sourcepath) + cwdStack.append(dirname(sourcepath)) + super(SourceFileLoader, self).exec_module(module) + cwdStack.pop() -class Invocation: - name = None - callback = None - types = None - ins = None - outs = None - binding = None - traits = None - attr = None - attrdeps = None - - def __init__(self): - self.attr = SimpleNamespace() - self.attrdeps = SimpleNamespace() - self.traits = set() - def __eq__(self, other): - return self.name is other.name +sys.meta_path.insert(0, PathFinderImpl()) - def __hash__(self): - return id(self.name) - def materialise(self, replacing=False): - if self in unmaterialisedTargets: - if not replacing and (self in materialisingStack): - print("Found dependency cycle:") - for i in materialisingStack: - print(f" {i.name}") - print(f" {self.name}") - sys.exit(1) - - materialisingStack.append(self) - - # Perform type conversion to the declared rule parameter types. - - try: - self.args = {} - for k, v in self.binding.arguments.items(): - if k != "kwargs": - t = self.types.get(k, None) - if t: - v = t(v).convert(self) - self.args[k] = v - else: - for kk, vv in v.items(): - t = self.types.get(kk, None) - if t: - vv = t(vv).convert(self) - self.args[kk] = vv - - # Actually call the callback. - - cwdStack.append(self.cwd) - self.callback(**self.args) - cwdStack.pop() - except BaseException as e: - print(f"Error materialising {self}: {self.callback}") - print(f"Arguments: {self.args}") - raise e - - if self.outs is None: - raise ABException(f"{self.name} didn't set self.outs") - - if self in unmaterialisedTargets: - unmaterialisedTargets.remove(self) - - materialisingStack.pop() - - def bubbleattr(self, attr, xs): - xs = targetsof(xs, cwd=self.cwd) - a = set() - if hasattr(self.attrdeps, attr): - a = getattr(self.attrdeps, attr) +class ABException(BaseException): + pass - for x in xs: - a.add(x) - setattr(self.attrdeps, attr, a) - def __repr__(self): - return "'%s'" % self.name +def error(message): + raise ABException(message) def Rule(func): @@ -139,303 +84,336 @@ def Rule(func): @functools.wraps(func) def wrapper(*, name=None, replaces=None, **kwargs): cwd = None - if name: - if ("+" in name) and not name.startswith("+"): - (cwd, _) = name.split("+", 1) + if "cwd" in kwargs: + cwd = kwargs["cwd"] + del kwargs["cwd"] + if not cwd: - cwd = cwdStack[-1] + if replaces: + cwd = replaces.cwd + else: + cwd = cwdStack[-1] if name: - i = Invocation() - if name.startswith("./"): - name = join(cwd, name) - elif "+" not in name: - name = join(cwd, "+" + name) - - i.name = name - i.localname = name.split("+")[-1] - - if name in targets: - raise ABException(f"target {i.name} has already been defined") - targets[name] = i + if name[0] != "+": + name = "+" + name + t = Target(cwd, join(cwd, name)) + + assert ( + t.name not in targets + ), f"target {t.name} has already been defined" + targets[t.name] = t elif replaces: - i = replaces - name = i.name + t = replaces else: raise ABException("you must supply either 'name' or 'replaces'") - i.cwd = cwd - i.sentinel = "$(OBJ)/.sentinels/" + name + ".mark" - i.types = func.__annotations__ - i.callback = func - i.traits.add(func.__name__) + t.cwd = cwd + t.types = func.__annotations__ + t.callback = func + t.traits.add(func.__name__) - i.binding = sig.bind(name=name, self=i, **kwargs) - i.binding.apply_defaults() + t.binding = sig.bind(name=name, self=t, **kwargs) + t.binding.apply_defaults() - unmaterialisedTargets.add(i) + unmaterialisedTargets[t] = None if replaces: - i.materialise(replacing=True) - return i + t.materialise(replacing=True) + return t defaultGlobals[func.__name__] = wrapper return wrapper -class Type: - def __init__(self, value): - self.value = value +class Target: + def __init__(self, cwd, name): + if verbose: + print("rule('%s', cwd='%s'" % (name, cwd)) + self.name = name + self.localname = self.name.rsplit("+")[-1] + self.traits = set() + self.dir = join("$(OBJ)", name) + self.ins = [] + self.outs = [] + self.materialised = False + self.args = {} + def __eq__(self, other): + return self.name is other.name -class List(Type): - def convert(self, invocation): - value = self.value - if not value: - return [] - if type(value) is str: - return [value] - return list(value) + def __hash__(self): + return id(self) + def __repr__(self): + return f"Target('{self.name}')" + + def templateexpand(selfi, s): + class Formatter(string.Formatter): + def get_field(self, name, a1, a2): + return ( + eval(name, selfi.callback.__globals__, selfi.args), + False, + ) + + def format_field(self, value, format_spec): + if not value: + return "" + if type(value) == str: + return value + if isinstance(value, (set, tuple)): + value = list(value) + if type(value) != list: + value = [value] + return " ".join( + [selfi.templateexpand(f) for f in filenamesof(value)] + ) + + return Formatter().format(s) -class Targets(Type): - def convert(self, invocation): - value = self.value - if not value: - return [] - if type(value) is str: - value = [value] - if type(value) is list: - value = targetsof(value, cwd=invocation.cwd) - return value + def materialise(self, replacing=False): + if self not in unmaterialisedTargets: + return + + if not replacing and self in materialisingStack: + print("Found dependency cycle:") + for i in materialisingStack: + print(f" {i.name}") + print(f" {self.name}") + sys.exit(1) + materialisingStack.append(self) + + # Perform type conversion to the declared rule parameter types. + + try: + self.args = {} + for k, v in self.binding.arguments.items(): + if k != "kwargs": + t = self.types.get(k, None) + if t: + v = t.convert(v, self) + self.args[k] = copy(v) + else: + for kk, vv in v.items(): + t = self.types.get(kk, None) + if t: + vv = t.convert(v, self) + self.args[kk] = copy(vv) + self.args["name"] = self.name + self.args["dir"] = self.dir + self.args["self"] = self + # Actually call the callback. -class Target(Type): - def convert(self, invocation): - value = self.value - if not value: - return None - return targetof(value, cwd=invocation.cwd) + cwdStack.append(self.cwd) + self.callback( + **{k: v for k, v in self.args.items() if k not in {"dir"}} + ) + cwdStack.pop() + except BaseException as e: + print(f"Error materialising {self}: {self.callback}") + print(f"Arguments: {self.args}") + raise e + if self.outs is None: + raise ABException(f"{self.name} didn't set self.outs") -class TargetsMap(Type): - def convert(self, invocation): - value = self.value - if not value: - return {} - if type(value) is dict: - return { - k: targetof(v, cwd=invocation.cwd) for k, v in value.items() - } - raise ABException(f"wanted a dict of targets, got a {type(value)}") - + if self in unmaterialisedTargets: + del unmaterialisedTargets[self] + materialisingStack.pop() + self.materialised = True -def flatten(*xs): - def recurse(xs): - for x in xs: - if isinstance(x, Iterable) and not isinstance(x, (str, bytes)): - yield from recurse(x) + def convert(value, target): + if not value: + return None + return target.targetof(value) + + def targetof(self, value): + if isinstance(value, str) and (value[0] == "="): + value = join(self.dir, value[1:]) + + return targetof(value, self.cwd) + + +def _filetarget(value, cwd): + if value in targets: + return targets[value] + + t = Target(cwd, value) + t.outs = [value] + targets[value] = t + return t + + +def targetof(value, cwd=None): + if not cwd: + cwd = cwdStack[-1] + if isinstance(value, Path): + value = value.as_posix() + if isinstance(value, Target): + t = value + else: + assert ( + value[0] != "=" + ), "can only use = for targets associated with another target" + + if value.startswith("."): + # Check for local rule. + if value.startswith(".+"): + value = normpath(join(cwd, value[1:])) + # Check for local path. + elif value.startswith("./"): + value = normpath(join(cwd, value)) + # Explicit directories are always raw files. + elif value.endswith("/"): + return _filetarget(value, cwd) + # Anything starting with a variable expansion is always a raw file. + elif value.startswith("$"): + return _filetarget(value, cwd) + + # If this is not a rule lookup... + if "+" not in value: + # ...and if the value is pointing at a directory without a trailing /, + # it's a shorthand rule lookup. + if isdir(value): + value = value + "+" + basename(value) + # Otherwise it's an absolute file. else: - yield x + return _filetarget(value, cwd) - return list(recurse(xs)) + # At this point we have the fully qualified name of a rule. + (path, target) = value.rsplit("+", 1) + value = join(path, "+" + target) + if value not in targets: + # Load the new build file. -def fileinvocation(s): - i = Invocation() - i.name = s - i.outs = [s] - targets[s] = i - return i + path = join(path, "build.py") + loadbuildfile(path) + assert ( + value in targets + ), f"build file at '{path}' doesn't contain '+{target}' when trying to resolve '{value}'" + t = targets[value] -def targetof(s, cwd=None): - if isinstance(s, Invocation): - s.materialise() - return s + t.materialise() + return t - if type(s) != str: - raise ABException("parameter of targetof is not a single target") - - if s in targets: - t = targets[s] - t.materialise() - return t - - if s.startswith("."): - if cwd == None: - raise ABException( - "relative target names can't be used in targetof without supplying cwd" - ) - if s.startswith(".+"): - s = cwd + s[1:] - elif s.startswith("./"): - s = normpath(join(cwd, s)) - - elif s.endswith("/"): - return fileinvocation(s) - elif s.startswith("$"): - return fileinvocation(s) - - if "+" not in s: - if isdir(s): - s = s + "+" + basename(s) - else: - return fileinvocation(s) - - (path, target) = s.split("+", 2) - s = join(path, "+" + target) - loadbuildfile(join(path, "build.py")) - if not s in targets: - raise ABException( - f"build file at {path} doesn't contain +{target} when trying to resolve {s}" - ) - i = targets[s] - i.materialise() - return i +class Targets: + def convert(value, target): + if not value: + return [] + assert isinstance( + value, (list, tuple) + ), "cannot convert non-list to Targets" + return [target.targetof(x) for x in flatten(value)] -def targetsof(*xs, cwd=None): - return flatten([targetof(x, cwd) for x in flatten(xs)]) +class TargetsMap: + def convert(value, target): + if not value: + return {} + output = {k: target.targetof(v) for k, v in value.items()} + for k, v in output.items(): + assert ( + len(filenamesof([v])) == 1 + ), f"targets of a TargetsMap used as an argument of {target} with key '{k}' must contain precisely one output file, but was {filenamesof([v])}" + return output -def filenamesof(*xs): - s = [] - for t in flatten(xs): - if type(t) == str: - t = normpath(t) - s += [t] - else: - s += [f for f in [normpath(f) for f in filenamesof(t.outs)]] - return s +def loadbuildfile(filename): + filename = filename.replace("/", ".").removesuffix(".py") + builtins.__import__(filename) -def filenamesmatchingof(xs, pattern): - return fnmatch.filter(filenamesof(xs), pattern) +def flatten(items): + def generate(xs): + for x in xs: + if isinstance(x, Iterable) and not isinstance(x, (str, bytes)): + yield from generate(x) + else: + yield x -def targetswithtraitsof(xs, trait): - return [target for target in targetsof(xs) if trait in target.traits] + return list(generate(items)) -def targetnamesof(*xs): - s = [] - for x in flatten(xs): - if type(x) == str: - x = normpath(x) - if x not in s: - s += [x] - else: - if x.name not in s: - s += [x.name] - return s +def targetnamesof(items): + if not isinstance(items, (list, tuple, set)): + error("argument of filenamesof is not a list/tuple/set") + return [t.name for t in items] -def filenameof(x): - xs = filenamesof(x) - if len(xs) != 1: - raise ABException("expected a single item") - return xs[0] +def filenamesof(items): + if not isinstance(items, (list, tuple, set)): + error("argument of filenamesof is not a list/tuple/set") -def bubbledattrsof(x, attr): - x = targetsof(x) - alltargets = set() - pending = set(x) if isinstance(x, Iterable) else {x} - while pending: - t = pending.pop() - if t not in alltargets: - alltargets.add(t) - if hasattr(t.attrdeps, attr): - pending.update(getattr(t.attrdeps, attr)) + def generate(xs): + for x in xs: + if isinstance(x, Target): + yield from generate(x.outs) + else: + yield x - values = [] - for t in alltargets: - if hasattr(t.attr, attr): - values += getattr(t.attr, attr) - return values + return list(generate(items)) -def stripext(path): - return splitext(path)[0] +def filenameof(x): + xs = filenamesof(x.outs) + assert ( + len(xs) == 1 + ), f"tried to use filenameof() on {x} which does not have exactly one output: {x.outs}" + return xs[0] def emit(*args): - outputFp.write(" ".join(flatten(args))) + outputFp.write(" ".join(args)) outputFp.write("\n") -def templateexpand(s, invocation): - class Formatter(string.Formatter): - def get_field(self, name, a1, a2): - return ( - eval(name, invocation.callback.__globals__, invocation.args), - False, - ) - - def format_field(self, value, format_spec): - if type(self) == str: - return value - return " ".join( - [templateexpand(f, invocation) for f in filenamesof(value)] - ) - - return Formatter().format(s) - +def emit_rule(name, ins, outs, cmds=[], label=None): + fins = filenamesof(ins) + fouts = filenamesof(outs) + nonobjs = [f for f in fouts if not f.startswith("$(OBJ)")] -def emitter_rule(rule, ins, outs, deps=[]): emit("") - emit(".PHONY:", rule.name) - emit(rule.name, ":", rule.sentinel) - - emit( - rule.sentinel, - # filenamesof(outs) if outs else [], - ":", - filenamesof(ins), - filenamesof(deps), - ) - - -def emitter_endrule(rule, outs): - emit("\t$(hide) mkdir -p", dirname(rule.sentinel)) - emit("\t$(hide) touch", rule.sentinel) - - for f in filenamesof(outs): - emit(".SECONDARY:", f) - emit(f, ":", rule.sentinel, ";") - - -def emitter_label(s): - emit("\t$(hide)", "$(ECHO)", s) - + if nonobjs: + emit("clean::") + emit("\t$(hide) rm -f", *nonobjs) -def emitter_exec(cs): - for c in cs: - emit("\t$(hide)", c) + emit(".PHONY:", name) + if outs: + emit(name, ":", *fouts) + if cmds: + emit(*fouts, "&:", *fins) + else: + emit(*fouts, ":", *fins) + if label: + emit("\t$(hide)", "$(ECHO)", label) + for c in cmds: + emit("\t$(hide)", c) + else: + assert len(cmds) == 0, "rules with no outputs cannot have commands" + emit(name, ":", *fins) -def unmake(*ss): - return [ - re.sub(r"\$\(([^)]*)\)", r"$\1", s) for s in flatten(filenamesof(ss)) - ] + emit("") @Rule def simplerule( self, name, - ins: Targets = None, - outs: List = [], - deps: Targets = None, - commands: List = [], + ins: Targets = [], + outs: Targets = [], + deps: Targets = [], + commands=[], label="RULE", **kwargs, ): self.ins = ins self.outs = outs self.deps = deps - emitter_rule(self, ins + deps, outs) - emitter_label(templateexpand("{label} {name}", self)) dirs = [] cs = [] @@ -447,100 +425,69 @@ def simplerule( cs = [("mkdir -p %s" % dir) for dir in dirs] for c in commands: - cs += [templateexpand(c, self)] - - emitter_exec(cs) - emitter_endrule(self, outs) - - -@Rule -def normalrule( - self, - name=None, - ins: Targets = None, - deps: Targets = None, - outs: List = [], - label="RULE", - objdir=None, - commands: List = [], - **kwargs, -): - objdir = objdir or join("$(OBJ)", name) - - self.attr.objdir = objdir - simplerule( - replaces=self, - ins=ins, - deps=deps, - outs=[join(objdir, f) for f in outs], - label=label, - commands=commands, - **kwargs, + cs += [self.templateexpand(c)] + + emit_rule( + name=self.name, + ins=ins + deps, + outs=outs, + label=self.templateexpand("{label} {name}"), + cmds=cs, ) @Rule -def export(self, name=None, items: TargetsMap = {}, deps: Targets = None): - cs = [] - self.ins = [] - self.outs = [] +def export(self, name=None, items: TargetsMap = {}, deps: Targets = []): + ins = [] + outs = [] for dest, src in items.items(): + dest = self.targetof(dest) + outs += [dest] + destf = filenameof(dest) - dir = dirname(destf) - srcs = filenamesof(src) - if len(srcs) != 1: - raise ABException( - "a dependency of an export must have exactly one output file" - ) + srcs = filenamesof([src]) + assert ( + len(srcs) == 1 + ), "a dependency of an exported file must have exactly one output file" subrule = simplerule( - name=self.name + "/+" + destf, + name=f"{self.localname}/{destf}", + cwd=self.cwd, ins=[srcs[0]], outs=[destf], commands=["cp %s %s" % (srcs[0], destf)], label="CP", ) subrule.materialise() - emit("clean::") - emit("\t$(hide) rm -f", destf) - self.ins += [subrule] - - emitter_rule( - self, - self.ins, - self.outs, - [(d.outs if d.outs else d.sentinel) for d in deps], + simplerule( + replaces=self, + ins=outs + deps, + outs=["=sentinel"], + commands=["touch {outs[0]}"], + label="EXPORT", ) - emitter_endrule(self, self.outs) - - -def loadbuildfile(filename): - filename = filename.replace("/", ".").removesuffix(".py") - builtins.__import__(filename) - - -def load(filename): - loadbuildfile(filename) - callerglobals = inspect.stack()[1][0].f_globals - for k, v in defaultGlobals.items(): - callerglobals[k] = v def main(): parser = argparse.ArgumentParser() + parser.add_argument("-v", "--verbose", action="store_true") + parser.add_argument("-q", "--quiet", action="store_true") parser.add_argument("-o", "--output") parser.add_argument("files", nargs="+") - parser.add_argument("-t", "--targets", action="append") args = parser.parse_args() - if not args.targets: - raise ABException("no targets supplied") + + global verbose + verbose = args.verbose + + global quiet + quiet = args.quiet global outputFp outputFp = open(args.output, "wt") - for k in ("Rule", "Targets", "load", "filenamesof", "stripext"): + for k in ["Rule"]: defaultGlobals[k] = globals()[k] global __name__ @@ -550,12 +497,9 @@ def main(): for f in args.files: loadbuildfile(f) - for t in flatten([a.split(",") for a in args.targets]): - (path, target) = t.split("+", 2) - s = join(path, "+" + target) - if s not in targets: - raise ABException("target %s is not defined" % s) - targets[s].materialise() + while unmaterialisedTargets: + t = next(iter(unmaterialisedTargets)) + t.materialise() emit("AB_LOADED = 1\n") diff --git a/build/c.py b/build/c.py index d7a6c704..3710e5b8 100644 --- a/build/c.py +++ b/build/c.py @@ -1,20 +1,19 @@ from build.ab import ( - ABException, - List, Rule, Targets, TargetsMap, filenameof, - filenamesmatchingof, filenamesof, flatten, - normalrule, - bubbledattrsof, + simplerule, +) +from build.utils import ( + filenamesmatchingof, stripext, targetswithtraitsof, + collectattrs, ) from os.path import * -from types import SimpleNamespace class Toolchain: @@ -38,16 +37,18 @@ class HostToolchain: def cfileimpl(self, name, srcs, deps, suffix, commands, label, kind, cflags): - outleaf = stripext(basename(filenameof(srcs[0]))) + suffix + outleaf = "=" + stripext(basename(filenameof(srcs[0]))) + suffix + + cflags = collectattrs(targets=deps, name="caller_cflags", initial=cflags) - normalrule( + t = simplerule( replaces=self, ins=srcs, deps=deps, outs=[outleaf], label=label, commands=commands, - cflags=cflags + bubbledattrsof(deps, "caller_cflags"), + cflags=cflags, ) @@ -57,7 +58,7 @@ def cfile( name, srcs: Targets = None, deps: Targets = None, - cflags: List = [], + cflags=[], suffix=".o", toolchain=Toolchain, commands=None, @@ -76,7 +77,7 @@ def cxxfile( name, srcs: Targets = None, deps: Targets = None, - cflags: List = [], + cflags=[], suffix=".o", toolchain=Toolchain, commands=None, @@ -91,9 +92,9 @@ def cxxfile( ) -def findsources(name, srcs, deps, cflags, toolchain, filerule): +def findsources(name, srcs, deps, cflags, toolchain, filerule, cwd): headers = filenamesmatchingof(srcs, "*.h") - cflags = cflags + ["-I"+dirname(h) for h in headers] + cflags = cflags + ["-I" + dirname(h) for h in headers] deps = deps + headers objs = [] @@ -105,15 +106,16 @@ def findsources(name, srcs, deps, cflags, toolchain, filerule): deps=deps, cflags=cflags, toolchain=toolchain, + cwd=cwd, ) - for f in filenamesof(s) + for f in filenamesof([s]) if f.endswith(".c") or f.endswith(".cc") or f.endswith(".cpp") or f.endswith(".S") or f.endswith(".s") ] - if any(f.endswith(".o") for f in filenamesof(s)): + if any(f.endswith(".o") for f in filenamesof([s])): objs += [s] return objs @@ -124,7 +126,7 @@ def cheaders( self, name, hdrs: TargetsMap = None, - caller_cflags: List = None, + caller_cflags=[], deps: Targets = None, ): cs = [] @@ -132,27 +134,24 @@ def cheaders( outs = [] i = 0 for dest, src in hdrs.items(): - s = filenamesof(src) - if len(s) != 1: - raise ABException( - "the target of a header must return exactly one file" - ) + s = filenamesof([src]) + assert ( + len(s) == 1 + ), "the target of a header must return exactly one file" cs += ["cp {ins[" + str(i) + "]} {outs[" + str(i) + "]}"] - outs += [dest] + outs += ["=" + dest] i = i + 1 - r = normalrule( + r = simplerule( replaces=self, ins=ins, outs=outs, commands=cs, deps=deps, label="CHEADERS", + caller_cflags=caller_cflags + ["-I" + self.dir], ) - r.materialise() - self.attr.caller_cflags = caller_cflags + ["-I" + r.attr.objdir] - self.bubbleattr("caller_cflags", deps) def libraryimpl( @@ -190,27 +189,31 @@ def libraryimpl( deps = deps + [hr] objs = findsources( - name, + self.localname, srcs, targetswithtraitsof(deps, "cheaders"), - cflags + bubbledattrsof(deps, "caller_cflags"), + cflags, toolchain, kind, + self.cwd, ) - normalrule( + simplerule( replaces=self, ins=objs, - outs=[basename(name) + ".a"], + outs=[f"={self.localname}.a"], label=label, commands=commands, + caller_cflags=collectattrs( + targets=deps + ([hr] if hr else []), name="caller_cflags" + ), + caller_ldflags=collectattrs( + targets=deps, name="caller_ldflags", initial=caller_ldflags + ), ) self.outs = self.outs + (hr.outs if hr else []) self.traits.add("cheaders") - self.attr.caller_ldflags = caller_ldflags - self.bubbleattr("caller_ldflags", deps) - self.bubbleattr("caller_cflags", deps) @Rule @@ -220,10 +223,10 @@ def clibrary( srcs: Targets = None, deps: Targets = None, hdrs: TargetsMap = None, - caller_cflags: List = [], - caller_ldflags: List = [], - cflags: List = [], - ldflags: List = [], + caller_cflags=[], + caller_ldflags=[], + cflags=[], + ldflags=[], toolchain=Toolchain, commands=None, label=None, @@ -257,10 +260,10 @@ def cxxlibrary( srcs: Targets = None, deps: Targets = None, hdrs: TargetsMap = None, - caller_cflags: List = [], - caller_ldflags: List = [], - cflags: List = [], - ldflags: List = [], + caller_cflags=[], + caller_ldflags=[], + cflags=[], + ldflags=[], toolchain=Toolchain, commands=None, label=None, @@ -300,17 +303,20 @@ def programimpl( kind, ): ars = filenamesmatchingof(deps, "*.a") - ldflags = ldflags + bubbledattrsof(deps, "caller_ldflags") - cfiles = findsources(name, srcs, deps, cflags, toolchain, filerule) - normalrule( + cfiles = findsources( + self.localname, srcs, deps, cflags, toolchain, filerule, self.cwd + ) + simplerule( replaces=self, ins=cfiles + ars + ars, - outs=[basename(name) + "$(EXT)"], + outs=[f"={self.localname}$(EXT)"], deps=deps, label=toolchain.label + label, commands=commands, - ldflags=ldflags, + ldflags=collectattrs( + targets=deps, name="caller_ldflags", initial=ldflags + ), ) @@ -320,8 +326,8 @@ def cprogram( name, srcs: Targets = None, deps: Targets = None, - cflags: List = [], - ldflags: List = [], + cflags=[], + ldflags=[], toolchain=Toolchain, commands=None, label="CLINK", @@ -351,8 +357,8 @@ def cxxprogram( name, srcs: Targets = None, deps: Targets = None, - cflags: List = [], - ldflags: List = [], + cflags=[], + ldflags=[], toolchain=Toolchain, commands=None, label="CXXLINK", diff --git a/build/gpp.py b/build/gpp.py index b6b53c0a..602c0c45 100644 --- a/build/gpp.py +++ b/build/gpp.py @@ -1,4 +1,4 @@ -from build.ab import Rule, normalrule, Targets, filenamesof +from build.ab import Rule, simplerule, Targets, filenamesof from os.path import * @@ -6,10 +6,10 @@ def gpp(self, name, srcs: Targets = []): hdrs = set(["-I" + dirname(f) for f in filenamesof(srcs)]) - normalrule( + simplerule( replaces=self, ins=srcs, - outs=[self.localname + ".i"], + outs=[f"={self.localname}.i"], commands=[ "gpp --nostdinc -U '' '' '(' ',' ')' '(' ')' '$$' '' -M '$$' '\\n' ' ' ' ' '\\n' '(' ')' " + (" ".join(hdrs)) diff --git a/build/nasm.py b/build/nasm.py index 1a024907..8d7f6fb0 100644 --- a/build/nasm.py +++ b/build/nasm.py @@ -1,12 +1,12 @@ -from build.ab import normalrule, Rule, Targets, filenameof +from build.ab import simplerule, Rule, Targets, filenameof @Rule def nasm(self, name, srcs: Targets = []): - normalrule( + simplerule( replaces=self, ins=srcs, - outs=[self.localname + ".obj"], + outs=[f"={self.localname}.obj"], commands=["nasm -f obj -o {outs} {ins}"], label="NASM", ) diff --git a/build/pkg.py b/build/pkg.py index bff53cd7..b5709a9f 100644 --- a/build/pkg.py +++ b/build/pkg.py @@ -1,4 +1,4 @@ -from build.ab import Rule, emit, Target, bubbledattrsof, filenamesof +from build.ab import Rule, emit, Target, filenamesof from types import SimpleNamespace import os import subprocess @@ -14,68 +14,46 @@ ) -@Rule -def package(self, name, package=None, fallback: Target = None): - emit("ifeq ($(filter %s, $(PACKAGES)),)" % package) +def _package(self, name, package, fallback, prefix=""): + emit(f"ifeq ($(filter {package}, $({prefix}PACKAGES)),)") if fallback: - emit(f"PACKAGE_DEPS_{package} := ", filenamesof(fallback)) + emit(f"{prefix}PACKAGE_DEPS_{package} := ", *filenamesof([fallback])) emit( - f"PACKAGE_CFLAGS_{package} :=", - bubbledattrsof(fallback, "caller_cflags"), + f"{prefix}PACKAGE_CFLAGS_{package} :=", + *fallback.args.get("caller_cflags", []), ) emit( - f"PACKAGE_LDFLAGS_{package} := ", - bubbledattrsof(fallback, "caller_ldflags"), - f"$(filter %.a, $(PACKAGE_DEPS_{package}))", + f"{prefix}PACKAGE_LDFLAGS_{package} := ", + *fallback.args.get("caller_ldflags", []), + f"$(filter %.a, $({prefix}PACKAGE_DEPS_{package}))", ) else: emit(f"$(error Required package '{package}' not installed.)") emit("else") emit( - f"PACKAGE_CFLAGS_{package} := $(shell $(PKG_CONFIG) --cflags {package})" + f"{prefix}PACKAGE_CFLAGS_{package} := $(shell $({prefix}PKG_CONFIG) --cflags {package})" ) emit( - f"PACKAGE_LDFLAGS_{package} := $(shell $(PKG_CONFIG) --libs {package})" + f"{prefix}PACKAGE_LDFLAGS_{package} := $(shell $({prefix}PKG_CONFIG) --libs {package})" ) - emit(f"PACKAGE_DEPS_{package} :=") + emit(f"{prefix}PACKAGE_DEPS_{package} :=") emit("endif") + emit(f"{self.name}:") - self.attr.caller_cflags = [f"$(PACKAGE_CFLAGS_{package})"] - self.attr.caller_ldflags = [f"$(PACKAGE_LDFLAGS_{package})"] + self.args["caller_cflags"] = [f"$({prefix}PACKAGE_CFLAGS_{package})"] + self.args["caller_ldflags"] = [f"$({prefix}PACKAGE_LDFLAGS_{package})"] self.traits.add("clibrary") self.traits.add("cheaders") self.ins = [] - self.outs = [f"$(PACKAGE_DEPS_{package})"] + self.outs = [f"$({prefix}PACKAGE_DEPS_{package})"] @Rule -def hostpackage(self, name, package=None, fallback: Target = None): - emit("ifeq ($(filter %s, $(HOST_PACKAGES)),)" % package) - if fallback: - emit( - f"HOST_PACKAGE_CFLAGS_{package} :=", - bubbledattrsof(fallback, "caller_cflags"), - ) - emit( - f"HOST_PACKAGE_LDFLAGS_{package} := ", - bubbledattrsof(fallback, "caller_ldflags"), - ) - emit(f"HOST_PACKAGE_DEP_{package} := ", fallback.name) - else: - emit(f"$(error Required host package '{package}' not installed.)") - emit("else") - emit( - f"HOST_PACKAGE_CFLAGS_{package} := $(shell $(HOST_PKG_CONFIG) --cflags {package})" - ) - emit( - f"HOST_PACKAGE_LDFLAGS_{package} := $(shell $(HOST_PKG_CONFIG) --libs {package})" - ) - emit(f"HOST_PACKAGE_DEP_{package} := ") - emit("endif") +def package(self, name, package=None, fallback: Target = None): + _package(self, name, package, fallback) - self.attr.caller_cflags = [f"$(HOST_PACKAGE_CFLAGS_{package})"] - self.attr.caller_ldflags = [f"$(HOST_PACKAGE_LDFLAGS_{package})"] - self.ins = [] - self.outs = [f"$(HOST_PACKAGE_DEP_{package})"] +@Rule +def hostpackage(self, name, package=None, fallback: Target = None): + _package(self, name, package, fallback, "HOST_") diff --git a/build/protobuf.py b/build/protobuf.py index 1205c837..e8af278c 100644 --- a/build/protobuf.py +++ b/build/protobuf.py @@ -1,16 +1,8 @@ -from os.path import join -from build.ab import ( - Rule, - Targets, - emit, - normalrule, - filenamesof, - filenamesmatchingof, - bubbledattrsof, -) -from build.c import cxxlibrary +from build.ab import Rule, Targets, emit, simplerule, filenamesof +from build.utils import filenamesmatchingof, collectattrs from types import SimpleNamespace -import build.pkg +from os.path import join +import build.pkg # to get the protobuf package check emit( """ @@ -23,49 +15,85 @@ @Rule -def proto(self, name, srcs: Targets = None, deps: Targets = None): - normalrule( +def proto(self, name, srcs: Targets = [], deps: Targets = []): + simplerule( replaces=self, ins=srcs, - outs=[f"{name}.descriptor"], + outs=[f"={name}.descriptor"], deps=deps, commands=[ "$(PROTOC) --include_source_info --descriptor_set_out={outs[0]} {ins}" ], label="PROTO", + protosrcs=filenamesof(srcs), ) - self.attr.protosrcs = filenamesof(srcs) - self.bubbleattr("protosrcs", deps) @Rule -def protocc(self, name, srcs: Targets = None, deps: Targets = None): +def protocc(self, name, srcs: Targets = [], deps: Targets = []): outs = [] protos = [] - for f in filenamesmatchingof(bubbledattrsof(srcs, "protosrcs"), "*.proto"): + allsrcs = collectattrs(targets=srcs, name="protosrcs") + assert allsrcs, "no sources provided" + for f in filenamesmatchingof(allsrcs, "*.proto"): cc = f.replace(".proto", ".pb.cc") h = f.replace(".proto", ".pb.h") protos += [f] srcs += [f] - outs += [cc, h] + outs += ["=" + cc, "=" + h] - srcname = f"{name}_srcs" - objdir = join("$(OBJ)", srcname) - r = normalrule( - name=srcname, + r = simplerule( + name=f"{self.localname}_srcs", + cwd=self.cwd, ins=protos, outs=outs, deps=deps, - commands=["$(PROTOC) --cpp_out={self.attr.objdir} {ins}"], + commands=["$(PROTOC) --cpp_out={dir} {ins}"], label="PROTOCC", ) - headers = {f: join(objdir, f) for f in outs if f.endswith(".pb.h")} + headers = {f[1:]: join(r.dir, f[1:]) for f in outs if f.endswith(".pb.h")} + + from build.c import cxxlibrary cxxlibrary( replaces=self, srcs=[r], + deps=deps, hdrs=headers, - cflags=[f"-I{objdir}"], + ) + + +@Rule +def protojava(self, name, srcs: Targets = [], deps: Targets = []): + outs = [] + + allsrcs = collectattrs(targets=srcs, name="protosrcs") + assert allsrcs, "no sources provided" + protos = [] + for f in filenamesmatchingof(allsrcs, "*.proto"): + protos += [f] + srcs += [f] + + r = simplerule( + name=f"{self.localname}_srcs", + cwd=self.cwd, + ins=protos, + outs=[f"={self.localname}.srcjar"], + deps=deps, + commands=[ + "mkdir -p {dir}/srcs", + "$(PROTOC) --java_out={dir}/srcs {ins}", + "$(JAR) cf {outs[0]} -C {dir}/srcs .", + ], + label="PROTOJAVA", + ) + r.traits.add("srcjar") + + from build.java import javalibrary + + javalibrary( + replaces=self, + deps=[r] + deps, ) diff --git a/build/tass64.py b/build/tass64.py index b9039213..5b451e44 100644 --- a/build/tass64.py +++ b/build/tass64.py @@ -1,12 +1,12 @@ -from build.ab import normalrule, Rule, Targets, filenameof +from build.ab import simplerule, Rule, Targets, filenameof @Rule def tass64(self, name, srcs: Targets = []): - normalrule( + simplerule( replaces=self, ins=srcs, - outs=[self.localname + ".bin"], + outs=[f"={self.localname}.bin"], commands=[ "64tass --quiet --long-branch --ascii --case-sensitive --nostart -o {outs} {ins}" ], diff --git a/build/utils.py b/build/utils.py index 0ce1cbc6..ac57304e 100644 --- a/build/utils.py +++ b/build/utils.py @@ -1,13 +1,62 @@ -from build.ab import Rule, normalrule, Target, filenameof, Targets -from os.path import basename +from build.ab import ( + Rule, + Target, + Targets, + filenameof, + filenamesof, + cwdStack, + error, + simplerule, +) +from os.path import relpath, splitext, join, basename +from glob import glob +import fnmatch +import itertools + + +def filenamesmatchingof(xs, pattern): + return fnmatch.filter(filenamesof(xs), pattern) + + +def stripext(path): + return splitext(path)[0] + + +def targetswithtraitsof(xs, trait): + return [t for t in xs if trait in t.traits] + + +def collectattrs(*, targets, name, initial=[]): + s = set(initial) + for a in [t.args.get(name, []) for t in targets]: + s.update(a) + return list(s) + + +def itemsof(pattern, root=None, cwd=None): + if not cwd: + cwd = cwdStack[-1] + if not root: + root = "." + + pattern = join(cwd, pattern) + root = join(cwd, root) + + result = {} + for f in glob(pattern, recursive=True): + try: + result[relpath(f, root)] = f + except ValueError: + error(f"file '{f}' is not in root '{root}'") + return result @Rule def objectify(self, name, src: Target, symbol): - normalrule( + simplerule( replaces=self, ins=["build/_objectify.py", src], - outs=[basename(filenameof(src)) + ".h"], + outs=[f"={basename(filenameof(src))}.h"], commands=["$(PYTHON) {ins[0]} {ins[1]} " + symbol + " > {outs}"], label="OBJECTIFY", ) @@ -24,19 +73,19 @@ def test( label="TEST", ): if command: - normalrule( + simplerule( replaces=self, ins=[command], - outs=["sentinel"], + outs=["=sentinel"], commands=["{ins[0]}", "touch {outs}"], deps=deps, label=label, ) else: - normalrule( + simplerule( replaces=self, ins=ins, - outs=["sentinel"], + outs=["=sentinel"], commands=commands + ["touch {outs}"], deps=deps, label=label, diff --git a/build/yacc.py b/build/yacc.py index 7e3020d5..f6378cb2 100644 --- a/build/yacc.py +++ b/build/yacc.py @@ -1,12 +1,12 @@ -from build.ab import normalrule, Rule, Targets +from build.ab import simplerule, Rule, Targets @Rule def yacc(self, name, srcs: Targets = []): - normalrule( + simplerule( replaces=self, ins=srcs, - outs=["y.tab.c", "y.tab.h"], + outs=["=y.tab.c", "=y.tab.h"], commands=["bison -y -t -o {outs[0]} --defines={outs[1]} {ins}"], label="YACC", ) @@ -14,10 +14,10 @@ def yacc(self, name, srcs: Targets = []): @Rule def flex(self, name, srcs: Targets = []): - normalrule( + simplerule( replaces=self, ins=srcs, - outs=["lexer.c"], + outs=["=lexer.c"], commands=["flex -8 -Cem -s -t {ins[0]} > {outs[0]}"], label="FLEX", ) diff --git a/build/zip.py b/build/zip.py new file mode 100644 index 00000000..421a5a10 --- /dev/null +++ b/build/zip.py @@ -0,0 +1,37 @@ +from build.ab import ( + Rule, + simplerule, + TargetsMap, + filenameof, + emit, +) + +emit( + """ +ZIP ?= zip +ZIPNOTE ?= zipnote +""" +) + + +@Rule +def zip( + self, name, flags="", items: TargetsMap = {}, extension="zip", label="ZIP" +): + cs = ["rm -f {outs[0]}"] + + ins = [] + for k, v in items.items(): + cs += [ + "cat %s | $(ZIP) -q %s {outs[0]} -" % (filenameof(v), flags), + "printf '@ -\\n@=%s\\n' | $(ZIPNOTE) -w {outs[0]}" % k, + ] + ins += [v] + + simplerule( + replaces=self, + ins=ins, + outs=[f"={self.localname}." + extension], + commands=cs, + label=label, + ) diff --git a/dist/bbct/build.py b/dist/bbct/build.py index 243e12af..5f9f38ae 100644 --- a/dist/bbct/build.py +++ b/dist/bbct/build.py @@ -1,13 +1,13 @@ -from build.ab import export, Rule, Target, normalrule +from build.ab import export, Rule, Target, simplerule, targetof from tools.build import tocpm, mkdfs @Rule -def bbcify(self, name, src: Target = None): - normalrule( +def bbcify(self, name, *, src: Target): + simplerule( replaces=self, ins=[src], - outs=[self.localname + ".txt"], + outs=[f"={self.localname}.txt"], commands=[ r"""sed -e 's/include "\(.*\)\.coh"/include "h.\1"/' < {ins} | expand -t4 | tr '\n' '\r' > {outs}""" ], @@ -22,32 +22,32 @@ def bbcify(self, name, src: Target = None): mkdfs( name="ssd", flags=[ - ["-f", Target("./!boot")], + ["-f", targetof("./!boot")], [ "-f", - Target("src/cowfe+cowfe-for-16bit-with-bbct"), + targetof("src/cowfe+cowfe-for-16bit-with-bbct"), "-e0x400", "-l0x400", "-ncowfe", ], [ "-f", - Target("src/cowbe+cowbe-for-6502-with-bbct"), + targetof("src/cowbe+cowbe-for-6502-with-bbct"), "-e0x400", "-l0x400", "-ncowbe", ], [ "-f", - Target("src/cowlink+cowlink-for-bbct-with-bbct"), + targetof("src/cowlink+cowlink-for-bbct-with-bbct"), "-e0x400", "-l0x400", "-ncowlink", ], - ["-f", Target("rt/bbct+cowgolcoo"), "-no.cowgol"], - ["-f", Target(".+cowgolcoh"), "-nh.cowgol"], - ["-f", Target(".+commoncoh"), "-nh.common"], - ["-f", Target(".+mandelcow"), "-nw.source"], + ["-f", targetof("rt/bbct+cowgolcoo"), "-no.cowgol"], + ["-f", targetof(".+cowgolcoh"), "-nh.cowgol"], + ["-f", targetof(".+commoncoh"), "-nh.common"], + ["-f", targetof(".+mandelcow"), "-nw.source"], "-B3", ], ) diff --git a/examples/build.py b/examples/build.py index 3fda758b..22d18b0f 100644 --- a/examples/build.py +++ b/examples/build.py @@ -17,7 +17,7 @@ for prog in PROGRAMS: t = cowgol( name=prog + "-for-" + toolchain.localname, - srcs="./" + prog + ".cow", + srcs=["./" + prog + ".cow"], toolchain=toolchain, ) diff --git a/src/build.py b/src/build.py index 5e309743..aa8d6d7d 100644 --- a/src/build.py +++ b/src/build.py @@ -2,10 +2,10 @@ Rule, Target, Targets, - normalrule, + simplerule, filenamesof, - targetswithtraitsof, ) +from build.utils import targetswithtraitsof from os.path import * from types import SimpleNamespace import config @@ -34,20 +34,20 @@ def cowlib( dirs = set([dirname(f) for f in filenamesof(srcs)]) flags = [f"-I{dir}/" for dir in dirs] - cob = normalrule( + cob = simplerule( name=name + "/cowfe", - ins=[toolchain.cowfe, cow] + srcs, - outs=[self.localname + ".cob"], + ins=[toolchain.cowfe, cow] + srcs + deps, + outs=[f"={self.localname}.cob"], commands=[ "chronic {ins[0]} " + (" ".join(flags)) + " {ins[1]} {outs[0]}" ], label="COWFE-" + toolchain.localname.upper(), ) - normalrule( + simplerule( replaces=self, ins=[toolchain.cowbe, cob], - outs=[self.localname + ".coo"], + outs=[f"={self.localname}.coo"], commands=["chronic {ins[0]} {ins[1]} {outs}"], label="COWBE-" + toolchain.localname.upper(), ) @@ -57,10 +57,10 @@ def cowlib( def cowlink(self, name, deps: Targets = [], toolchain: Target = None): coos = filenamesof(targetswithtraitsof(deps, "cowlib")) - asm = normalrule( + asm = simplerule( name=name + "/cowlink", ins=[toolchain.cowlink] + [coos], - outs=[self.localname + toolchain.asmext], + outs=[f"={self.localname}{toolchain.asmext}"], commands=["chronic {ins[0]} -o {outs} {filenamesof(ins[1:])}"], label="COWLINK-" + toolchain.localname.upper(), ) @@ -88,71 +88,71 @@ def cowgol( @Rule def cowwrap(self, name, src: Target = None, toolchain: Target = "src+ncgen"): self.traits.add("cowlib") - normalrule( + simplerule( replaces=self, ins=[toolchain.cowwrap, src], - outs=["cowgol.coo"], + outs=["=cowgol.coo"], label="COWWRAP-" + toolchain.localname.upper(), commands=["chronic {ins[0]} {ins[1]} {outs}"], ) -normalrule( +simplerule( name="midcodesfecoh", ins=[ "scripts/mkmidcodescoh.lua", "scripts/libcowgol.lua", "src/midcodes.coh.tab", ], - outs=["midcodesfe.coh"], + outs=["=midcodesfe.coh"], commands=["lua {ins[0]} -- {ins[2]} {outs[0]} fe"], label="MKMIDCODESFE", ) -normalrule( +simplerule( name="midcodesbecoh", ins=[ "scripts/mkmidcodescoh.lua", "scripts/libcowgol.lua", "src/midcodes.coh.tab", ], - outs=["midcodesbe.coh"], + outs=["=midcodesbe.coh"], commands=["lua {ins[0]} -- {ins[2]} {outs[0]} be"], label="MKMIDCODESBE", ) -normalrule( +simplerule( name="coboutcoh", ins=[ "scripts/mkcobout.lua", "scripts/libcowgol.lua", "src/midcodes.coh.tab", ], - outs=["cobout.coh"], + outs=["=cobout.coh"], commands=["lua {ins[0]} -- {ins[2]} {outs[0]}"], label="MKCOBOUT", ) -normalrule( +simplerule( name="iburgcodes", ins=[ "scripts/mkiburgcodes.lua", "scripts/libcowgol.lua", "src/midcodes.coh.tab", ], - outs=["iburgcodes-coh.h"], + outs=["=iburgcodes-coh.h"], commands=["lua {ins[0]} -- {ins[2]} {outs[0]}"], label="MKIBURGCODES", ) -normalrule( +simplerule( name="cobincoh", ins=[ "scripts/mkcobin.lua", "scripts/libcowgol.lua", "src/midcodes.coh.tab", ], - outs=["cobin.coh"], + outs=["=cobin.coh"], commands=["lua {ins[0]} -- {ins[2]} {outs[0]}"], label="MKCOBIN", ) diff --git a/src/cowasm/build.py b/src/cowasm/build.py index 0abcdd22..4d1f4b8a 100644 --- a/src/cowasm/build.py +++ b/src/cowasm/build.py @@ -1,4 +1,4 @@ -from build.ab import normalrule, export +from build.ab import simplerule, export from src.build import cowgol from tools.newgen.build import newgencowgol from src.toolchains import TOOLCHAINS diff --git a/src/cowbdmp/build.py b/src/cowbdmp/build.py index ba76d276..d58afb39 100644 --- a/src/cowbdmp/build.py +++ b/src/cowbdmp/build.py @@ -1,4 +1,4 @@ -from build.ab import normalrule +from build.ab import simplerule from src.build import cowgol from src.toolchains import TOOLCHAINS diff --git a/src/cowbe/build.py b/src/cowbe/build.py index 8aa67388..5070bf69 100644 --- a/src/cowbe/build.py +++ b/src/cowbe/build.py @@ -1,4 +1,4 @@ -from build.ab import normalrule, export +from build.ab import simplerule, export from src.build import cowgol from src.toolchains import TOOLCHAINS from tools.newgen.build import newgencowgol diff --git a/src/cowdis/build.py b/src/cowdis/build.py index cc45a927..5e27bb1f 100644 --- a/src/cowdis/build.py +++ b/src/cowdis/build.py @@ -1,4 +1,4 @@ -from build.ab import normalrule +from build.ab import simplerule from src.build import cowgol from src.toolchains import TOOLCHAINS diff --git a/src/cowfe/build.py b/src/cowfe/build.py index e697b869..0410179d 100644 --- a/src/cowfe/build.py +++ b/src/cowfe/build.py @@ -1,4 +1,4 @@ -from build.ab import normalrule, export +from build.ab import simplerule, export from src.build import cowgol from src.toolchains import TOOLCHAINS from third_party.lemon.build import lemoncowgol @@ -17,10 +17,10 @@ lemoncowgol(name="parser", src="src/cowfe/parser.y") for arch in ARCHS: - normalrule( + simplerule( name="arch-" + arch, ins=["./arch" + arch + ".coh"], - outs=["arch.coh"], + outs=["=arch.coh"], commands=["cp {ins} {outs}"], label="COPY", ) diff --git a/src/cowlink/build.py b/src/cowlink/build.py index 5a835ba7..8eeb5864 100644 --- a/src/cowlink/build.py +++ b/src/cowlink/build.py @@ -1,4 +1,4 @@ -from build.ab import normalrule, export +from build.ab import simplerule, export from src.build import cowgol from src.toolchains import TOOLCHAINS @@ -21,10 +21,10 @@ ] for arch in ARCHS: - normalrule( + simplerule( name="arch-" + arch, ins=["./arch" + arch + ".coh"], - outs=["archlink.coh"], + outs=["=archlink.coh"], commands=["cp {ins} {outs}"], label="COPY", ) diff --git a/src/misc/build.py b/src/misc/build.py index 5602d882..cefc469b 100644 --- a/src/misc/build.py +++ b/src/misc/build.py @@ -1,4 +1,4 @@ -from build.ab import normalrule +from build.ab import simplerule from src.build import cowgol from src.toolchains import TOOLCHAINS diff --git a/src/toolchains.py b/src/toolchains.py index 2dfbc78d..dbec8c43 100644 --- a/src/toolchains.py +++ b/src/toolchains.py @@ -3,7 +3,7 @@ Target, Targets, export, - normalrule, + simplerule, filenamesof, filenameof, ) @@ -23,10 +23,10 @@ def cgen(self, name, srcs: Targets = []): def buildgasimpl(self, prefix, flags=""): - normalrule( + simplerule( replaces=self, ins=self.args["srcs"], - outs=[self.localname + ".elf"], + outs=[f"={self.localname}.elf"], commands=[ prefix + "-as " + flags + " -g {ins} -o {outs[0]}.s", prefix + "-ld -g {outs[0]}.s -o {outs[0]}", @@ -66,10 +66,10 @@ def buildtass64(self, name, srcs: Targets = None): def buildcowasmimpl(self, asm): - normalrule( + simplerule( replaces=self, ins=[asm] + self.args["srcs"], - outs=[self.localname + ".bin"], + outs=[f"={self.localname}.bin"], commands=["chronic {ins[0]} -o {outs[0]} {ins[1]}"], label="ASM", ) @@ -93,10 +93,10 @@ def buildnasm(self, name, srcs: Targets = None): def testimpl(self, dep, command): goodfile = self.args["goodfile"] - normalrule( + simplerule( replaces=self, ins=dep + [self.args["exe"]], - outs=[self.localname + ".bad"], + outs=[f"={self.localname}.bad"], commands=[ "timeout 5s " + command + " > {outs}; true", "diff -u -w {outs[0]} " + filenameof(goodfile), diff --git a/tests/build.py b/tests/build.py index 512cbb41..045b6cd1 100644 --- a/tests/build.py +++ b/tests/build.py @@ -1,4 +1,4 @@ -from build.ab import normalrule, Rule, Target, export +from build.ab import simplerule, Rule, Target, export from src.build import cowgol from src.toolchains import TOOLCHAINS @@ -71,10 +71,10 @@ def testsuite(self, name, toolchain: Target = None): ) ) - normalrule( + simplerule( replaces=self, ins=tests, - outs=["stamp"], + outs=["=stamp"], commands=["touch {outs}"], label="TESTSUITE", ) diff --git a/third_party/djlink/build.py b/third_party/djlink/build.py index 15bc8b9c..5a148083 100644 --- a/third_party/djlink/build.py +++ b/third_party/djlink/build.py @@ -1,5 +1,5 @@ from build.c import cxxprogram -from build.ab import export, Rule, Targets, normalrule +from build.ab import export, Rule, Targets, simplerule cxxprogram( name="objdump", @@ -53,10 +53,10 @@ @Rule def djlink(self, name, srcs: Targets = []): - normalrule( + simplerule( replaces=self, ins=["third_party/djlink"] + srcs, - outs=[self.localname + ".bin"], + outs=[f"={self.localname}.bin"], commands=["{ins[0]} -o {outs} {ins[1]} > /dev/null"], label="DJLINK", ) diff --git a/third_party/lemon/build.py b/third_party/lemon/build.py index 0ea23d1b..7269b9bf 100644 --- a/third_party/lemon/build.py +++ b/third_party/lemon/build.py @@ -1,4 +1,4 @@ -from build.ab import Rule, Target, normalrule +from build.ab import Rule, Target, simplerule from build.c import cprogram cprogram( @@ -14,21 +14,21 @@ @Rule def lemon(self, name, src: Target = None): - normalrule( + simplerule( replaces=self, ins=["third_party/lemon+lemon", "third_party/lemon/lempar.c", src], - outs=[self.localname + ".c", self.localname + ".h"], - commands=["{ins[0]} -T{ins[1]} -d{dirname(outs[0])} {ins[2]}"], + outs=[f"={self.localname}.c", f"={self.localname}.h"], + commands=["{ins[0]} -T{ins[1]} -d{dir} {ins[2]}"], label="LEMON", ) @Rule def lemoncowgol(self, name, src: Target = None): - normalrule( + simplerule( replaces=self, ins=["third_party/lemon+lemon-cowgol", "src/cowfe/lempar.coh", src], - outs=[self.localname + ".coh", self.localname + ".tokens.coh"], - commands=["{ins[0]} -T{ins[1]} -d{dirname(outs[0])} {ins[2]}"], + outs=[f"={self.localname}.coh", f"={self.localname}.tokens.coh"], + commands=["{ins[0]} -T{ins[1]} -d{dir} {ins[2]}"], label="LEMON-COWGOL", ) diff --git a/third_party/musashi/build.py b/third_party/musashi/build.py index 9b874861..2fbd9c83 100644 --- a/third_party/musashi/build.py +++ b/third_party/musashi/build.py @@ -1,13 +1,14 @@ -from build.ab import Rule, Target, normalrule +from build.ab import Rule, Target, simplerule from build.c import clibrary, cprogram +from os.path import * cprogram(name="m68kmake", srcs=["./m68kmake.c"]) -normalrule( +simplerule( name="m68kops", ins=[".+m68kmake", "./m68k_in.c"], - outs=["m68kops.c", "m68kops.h"], - commands=["{ins[0]} {dirname(outs[0])} {ins[1]} > /dev/null"], + outs=["=m68kops.c", "=m68kops.h"], + commands=["{ins[0]} {dir} {ins[1]} > /dev/null"], label="MUSASHILIB", ) diff --git a/third_party/zmac/build.py b/third_party/zmac/build.py index d6b00e19..11ef1fbd 100644 --- a/third_party/zmac/build.py +++ b/third_party/zmac/build.py @@ -1,6 +1,6 @@ from build.yacc import yacc from build.c import cprogram -from build.ab import Rule, Targets, normalrule, filenameof +from build.ab import Rule, Targets, simplerule, filenameof from os.path import * yacc( @@ -16,14 +16,15 @@ @Rule -def zmac(self, name, srcs: Targets = []): - filename, ext = splitext(filenameof(srcs)) +def zmac(self, name, srcs: Targets): + assert len(srcs) == 1, "zmac can only take one source file" + filename, ext = splitext(filenameof(srcs[0])) archflag = "-z" if (ext == ".z80") else "-8" - normalrule( + simplerule( replaces=self, ins=["third_party/zmac", srcs[0]], - outs=[self.localname + ".cim"], + outs=[f"={self.localname}.cim"], commands=[ "{ins[0]} -j -m " + archflag diff --git a/tools/build.py b/tools/build.py index 0d0c0201..047ac217 100644 --- a/tools/build.py +++ b/tools/build.py @@ -1,5 +1,5 @@ from build.c import cprogram -from build.ab import Rule, Target, Targets, normalrule, flatten, filenamesof +from build.ab import Rule, Target, Targets, simplerule, flatten, filenameof cprogram( name="mkadfs", @@ -14,10 +14,10 @@ @Rule def tocpm(self, name, src: Target = None): - normalrule( + simplerule( replaces=self, ins=["tools/tocpm.lua", src], - outs=[self.localname + ".txt"], + outs=[f"={self.localname}.txt"], commands=["lua {ins[0]} < {ins[1]} > {outs}"], label="TOCPM", ) @@ -29,15 +29,15 @@ def mkdfs(self, name, flags=[]): cmdline = ["{ins[0]}", "-O", "{outs}"] for f in flatten(flags): if isinstance(f, Target): - srcs += [f.convert(self)] - cmdline += filenamesof(f.convert(self)) + srcs += [f] + cmdline += filenameof(f) else: cmdline += [f] - normalrule( + simplerule( replaces=self, ins=["tools+mkdfs"] + srcs, - outs=[self.localname + ".ssd"], + outs=[f"={self.localname}.ssd"], commands=[" ".join(cmdline)], label="MKDFS", ) diff --git a/tools/cpmemu/build.py b/tools/cpmemu/build.py index afa4d7eb..d7de79e8 100644 --- a/tools/cpmemu/build.py +++ b/tools/cpmemu/build.py @@ -15,6 +15,6 @@ "./biosbdos.c", ".+biosbdosdata", ], - ldflags= ["-lreadline"], - deps = ["third_party/z80ex"], + ldflags=["-lreadline"], + deps=["third_party/z80ex"], ) diff --git a/tools/fuzix6303emu/build.py b/tools/fuzix6303emu/build.py index 1e260f93..a70e102f 100644 --- a/tools/fuzix6303emu/build.py +++ b/tools/fuzix6303emu/build.py @@ -8,5 +8,5 @@ "./globals.h", ], deps=["third_party/rc2014emu"], - ldflags= ["-lreadline"], + ldflags=["-lreadline"], ) diff --git a/tools/newgen/build.py b/tools/newgen/build.py index 641e5591..c890261b 100644 --- a/tools/newgen/build.py +++ b/tools/newgen/build.py @@ -1,4 +1,4 @@ -from build.ab import Rule, Targets, normalrule +from build.ab import Rule, Targets, simplerule from build.c import cprogram from build.yacc import flex from build.gpp import gpp @@ -18,7 +18,8 @@ ".+lexer", "src+iburgcodes", ], - cflags=["-DCOWGOL"], ldflags=["-lfl"], + cflags=["-DCOWGOL"], + ldflags=["-lfl"], ) @@ -26,10 +27,10 @@ def newgencowgol(self, name, srcs: Targets = []): preprocessed = gpp(name=name + "/preprocessed", srcs=srcs) - normalrule( + simplerule( replaces=self, ins=["tools/newgen", preprocessed], - outs=["inssel.coh", "inssel.decl.coh"], + outs=["=inssel.coh", "=inssel.decl.coh"], commands=["{ins[0]} {ins[1]} {outs[0]} {outs[1]}"], label="NEWGEN", ) diff --git a/tools/obpemu/build.py b/tools/obpemu/build.py index 4ab54f6c..445a3254 100644 --- a/tools/obpemu/build.py +++ b/tools/obpemu/build.py @@ -1,7 +1,5 @@ from build.c import cprogram cprogram( - name="obpemu", - srcs=["./emulator.c", "./main.c"], - ldflags=["-lreadline"] + name="obpemu", srcs=["./emulator.c", "./main.c"], ldflags=["-lreadline"] )