Update to build with ab.

This commit is contained in:
David Given
2024-03-29 23:53:05 +01:00
parent df83b558bf
commit c115de9d40
13 changed files with 395 additions and 244 deletions

View File

@@ -44,7 +44,7 @@ all: +all README.md
binaries: all binaries: all
tests: all tests: all
README.md: $(OBJ)/scripts+mkdocindex/scripts+mkdocindex$(EXT) README.md: $(OBJ)/scripts/+mkdocindex/+mkdocindex$(EXT)
@echo MKDOC $@ @echo MKDOC $@
@csplit -s -f$(OBJ)/README. README.md '/<!-- FORMATSSTART -->/' '%<!-- FORMATSEND -->%' @csplit -s -f$(OBJ)/README. README.md '/<!-- FORMATSSTART -->/' '%<!-- FORMATSEND -->%'
@(cat $(OBJ)/README.00 && $< && cat $(OBJ)/README.01) > README.md @(cat $(OBJ)/README.00 && $< && cat $(OBJ)/README.01) > README.md

View File

@@ -9,9 +9,19 @@ CXX ?= g++
AR ?= ar AR ?= ar
CFLAGS ?= -g -Og CFLAGS ?= -g -Og
LDFLAGS ?= -g LDFLAGS ?= -g
hide = @
PKG_CONFIG ?= pkg-config PKG_CONFIG ?= pkg-config
ECHO ?= echo ECHO ?= echo
TARGETS ?= +all
ifdef VERBOSE
hide =
else
ifdef V
hide =
else
hide = @
endif
endif
ifeq ($(OS), Windows_NT) ifeq ($(OS), Windows_NT)
EXT ?= .exe EXT ?= .exe
@@ -20,6 +30,9 @@ EXT ?=
include $(OBJ)/build.mk include $(OBJ)/build.mk
MAKEFLAGS += -r
.DELETE_ON_ERROR:
.PHONY: update-ab .PHONY: update-ab
update-ab: update-ab:
@echo "Press RETURN to update ab from the repository, or CTRL+C to cancel." \ @echo "Press RETURN to update ab from the repository, or CTRL+C to cancel." \
@@ -33,10 +46,10 @@ clean::
$(hide) rm -rf $(OBJ) bin $(hide) rm -rf $(OBJ) bin
export PYTHONHASHSEED = 1 export PYTHONHASHSEED = 1
build-files = $(shell find . -name 'build.py') build/*.py config.py build-files = $(shell find . -name 'build.py') $(wildcard build/*.py) $(wildcard config.py)
$(OBJ)/build.mk: Makefile $(build-files) $(OBJ)/build.mk: Makefile $(build-files)
@echo "AB" @echo "AB"
@mkdir -p $(OBJ) @mkdir -p $(OBJ)
$(hide) $(PYTHON) -X pycache_prefix=$(OBJ) build/ab.py -t +all -o $@ \ $(hide) $(PYTHON) -X pycache_prefix=$(OBJ) build/ab.py $(patsubst %,-t %,$(TARGETS)) -o $@ \
build.py || rm -f $@ build.py || rm -f $@

View File

@@ -2,7 +2,6 @@ from collections.abc import Iterable, Sequence
from os.path import * from os.path import *
from types import SimpleNamespace from types import SimpleNamespace
import argparse import argparse
import copy
import functools import functools
import importlib import importlib
import importlib.abc import importlib.abc
@@ -10,10 +9,10 @@ import importlib.util
import inspect import inspect
import re import re
import sys import sys
import types
import pathlib
import builtins import builtins
import os import string
import fnmatch
import traceback
defaultGlobals = {} defaultGlobals = {}
targets = {} targets = {}
@@ -52,27 +51,6 @@ class ABException(BaseException):
pass pass
class ParameterList(Sequence):
def __init__(self, parent=[]):
self.data = parent
def __getitem__(self, i):
return self.data[i]
def __len__(self):
return len(self.data)
def __str__(self):
return " ".join(self.data)
def __add__(self, other):
newdata = self.data.copy() + other
return ParameterList(newdata)
def __repr__(self):
return f"<PList: {self.data}>"
class Invocation: class Invocation:
name = None name = None
callback = None callback = None
@@ -80,6 +58,20 @@ class Invocation:
ins = None ins = None
outs = None outs = None
binding = 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
def __hash__(self):
return id(self.name)
def materialise(self, replacing=False): def materialise(self, replacing=False):
if self in unmaterialisedTargets: if self in unmaterialisedTargets:
@@ -115,9 +107,7 @@ class Invocation:
self.callback(**self.args) self.callback(**self.args)
cwdStack.pop() cwdStack.pop()
except BaseException as e: except BaseException as e:
print( print(f"Error materialising {self}: {self.callback}")
f"Error materialising {self} ({id(self)}): {self.callback}"
)
print(f"Arguments: {self.args}") print(f"Arguments: {self.args}")
raise e raise e
@@ -129,8 +119,18 @@ class Invocation:
materialisingStack.pop() 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)
for x in xs:
a.add(x)
setattr(self.attrdeps, attr, a)
def __repr__(self): def __repr__(self):
return "<Invocation %s>" % self.name return "'%s'" % self.name
def Rule(func): def Rule(func):
@@ -150,7 +150,7 @@ def Rule(func):
if name.startswith("./"): if name.startswith("./"):
name = join(cwd, name) name = join(cwd, name)
elif "+" not in name: elif "+" not in name:
name = cwd + "+" + name name = join(cwd, "+" + name)
i.name = name i.name = name
i.localname = name.split("+")[-1] i.localname = name.split("+")[-1]
@@ -162,12 +162,13 @@ def Rule(func):
i = replaces i = replaces
name = i.name name = i.name
else: else:
raise ABException("you must supply either name or replaces") raise ABException("you must supply either 'name' or 'replaces'")
i.cwd = cwd i.cwd = cwd
i.sentinel = "$(OBJ)/.sentinels/" + name + ".mark"
i.types = func.__annotations__ i.types = func.__annotations__
i.callback = func i.callback = func
setattr(i, func.__name__, SimpleNamespace()) i.traits.add(func.__name__)
i.binding = sig.bind(name=name, self=i, **kwargs) i.binding = sig.bind(name=name, self=i, **kwargs)
i.binding.apply_defaults() i.binding.apply_defaults()
@@ -186,9 +187,21 @@ class Type:
self.value = value self.value = value
class List(Type):
def convert(self, invocation):
value = self.value
if not value:
return []
if type(value) is str:
return [value]
return list(value)
class Targets(Type): class Targets(Type):
def convert(self, invocation): def convert(self, invocation):
value = self.value value = self.value
if not value:
return []
if type(value) is str: if type(value) is str:
value = [value] value = [value]
if type(value) is list: if type(value) is list:
@@ -207,6 +220,8 @@ class Target(Type):
class TargetsMap(Type): class TargetsMap(Type):
def convert(self, invocation): def convert(self, invocation):
value = self.value value = self.value
if not value:
return {}
if type(value) is dict: if type(value) is dict:
return { return {
k: targetof(v, cwd=invocation.cwd) for k, v in value.items() k: targetof(v, cwd=invocation.cwd) for k, v in value.items()
@@ -233,20 +248,29 @@ def fileinvocation(s):
return i return i
def targetof(s, cwd): def targetof(s, cwd=None):
if isinstance(s, Invocation): if isinstance(s, Invocation):
s.materialise() s.materialise()
return s return s
if type(s) != str:
raise ABException("parameter of targetof is not a single target")
if s in targets: if s in targets:
t = targets[s] t = targets[s]
t.materialise() t.materialise()
return t return t
if s.startswith(".+"): if s.startswith("."):
s = cwd + s[1:] if cwd == None:
elif s.startswith("./"): raise ABException(
s = normpath(join(cwd, s)) "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("/"): elif s.endswith("/"):
return fileinvocation(s) return fileinvocation(s)
elif s.startswith("$"): elif s.startswith("$"):
@@ -259,15 +283,18 @@ def targetof(s, cwd):
return fileinvocation(s) return fileinvocation(s)
(path, target) = s.split("+", 2) (path, target) = s.split("+", 2)
s = join(path, "+" + target)
loadbuildfile(join(path, "build.py")) loadbuildfile(join(path, "build.py"))
if not s in targets: if not s in targets:
raise ABException(f"build file at {path} doesn't contain +{target}") raise ABException(
f"build file at {path} doesn't contain +{target} when trying to resolve {s}"
)
i = targets[s] i = targets[s]
i.materialise() i.materialise()
return i return i
def targetsof(*xs, cwd): def targetsof(*xs, cwd=None):
return flatten([targetof(x, cwd) for x in flatten(xs)]) return flatten([targetof(x, cwd) for x in flatten(xs)])
@@ -282,6 +309,14 @@ def filenamesof(*xs):
return s return s
def filenamesmatchingof(xs, pattern):
return fnmatch.filter(filenamesof(xs), pattern)
def targetswithtraitsof(xs, trait):
return [target for target in targetsof(xs) if trait in target.traits]
def targetnamesof(*xs): def targetnamesof(*xs):
s = [] s = []
for x in flatten(xs): for x in flatten(xs):
@@ -302,6 +337,24 @@ def filenameof(x):
return xs[0] return xs[0]
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))
values = []
for t in alltargets:
if hasattr(t.attr, attr):
values += getattr(t.attr, attr)
return values
def stripext(path): def stripext(path):
return splitext(path)[0] return splitext(path)[0]
@@ -312,30 +365,43 @@ def emit(*args):
def templateexpand(s, invocation): def templateexpand(s, invocation):
class Converter: class Formatter(string.Formatter):
def __getitem__(self, key): def get_field(self, name, a1, a2):
if key == "self": return (
return invocation eval(name, invocation.callback.__globals__, invocation.args),
f = filenamesof(invocation.args[key]) False,
if isinstance(f, Sequence): )
f = ParameterList(f)
return f
return eval("f%r" % s, invocation.callback.__globals__, Converter()) 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 emitter_rule(name, ins, outs, deps=[]): def emitter_rule(rule, ins, outs, deps=[]):
emit("") emit("")
emit(".PHONY:", name) emit(".PHONY:", rule.name)
if outs: emit(rule.name, ":", rule.sentinel)
emit(name, ":", filenamesof(outs), ";")
emit(filenamesof(outs), "&:", filenamesof(ins), filenamesof(deps)) emit(
else: rule.sentinel,
emit(name, "&:", filenamesof(ins), filenamesof(deps)) # filenamesof(outs) if outs else [],
":",
filenamesof(ins),
filenamesof(deps),
)
def emitter_endrule(name): def emitter_endrule(rule, outs):
pass emit("\t$(hide) mkdir -p", dirname(rule.sentinel))
emit("\t$(hide) touch", rule.sentinel)
for f in filenamesof(outs):
emit(f, ":", rule.sentinel, ";")
def emitter_label(s): def emitter_label(s):
@@ -357,47 +423,50 @@ def unmake(*ss):
def simplerule( def simplerule(
self, self,
name, name,
ins: Targets = [], ins: Targets = None,
outs=[], outs: List = [],
deps: Targets = [], deps: Targets = None,
commands=[], commands: List = [],
label="RULE", label="RULE",
**kwargs, **kwargs,
): ):
self.ins = ins self.ins = ins
self.outs = outs self.outs = outs
self.deps = deps self.deps = deps
emitter_rule(self.name, ins + deps, outs) emitter_rule(self, ins + deps, outs)
emitter_label(templateexpand("{label} {name}", self)) emitter_label(templateexpand("{label} {name}", self))
dirs = [] dirs = []
cs = []
for out in filenamesof(outs): for out in filenamesof(outs):
dir = dirname(out) dir = dirname(out)
if dir and dir not in dirs: if dir and dir not in dirs:
dirs += [dir] dirs += [dir]
cs = [("mkdir -p %s" % dir) for dir in dirs] cs = [("mkdir -p %s" % dir) for dir in dirs]
for c in commands: for c in commands:
cs += [templateexpand(c, self)] cs += [templateexpand(c, self)]
emitter_exec(cs) emitter_exec(cs)
emitter_endrule(self.name) emitter_endrule(self, outs)
@Rule @Rule
def normalrule( def normalrule(
self, self,
name=None, name=None,
ins: Targets = [], ins: Targets = None,
deps: Targets = [], deps: Targets = None,
outs=[], outs: List = [],
label="RULE", label="RULE",
objdir=None, objdir=None,
commands=[], commands: List = [],
**kwargs, **kwargs,
): ):
objdir = objdir or join("$(OBJ)", name) objdir = objdir or join("$(OBJ)", name)
self.normalrule.objdir = objdir self.attr.objdir = objdir
simplerule( simplerule(
replaces=self, replaces=self,
ins=ins, ins=ins,
@@ -410,9 +479,9 @@ def normalrule(
@Rule @Rule
def export(self, name=None, items: TargetsMap = {}, deps: Targets = []): def export(self, name=None, items: TargetsMap = {}, deps: Targets = None):
cs = [] cs = []
self.ins = items.values() self.ins = []
self.outs = [] self.outs = []
for dest, src in items.items(): for dest, src in items.items():
destf = filenameof(dest) destf = filenameof(dest)
@@ -424,23 +493,26 @@ def export(self, name=None, items: TargetsMap = {}, deps: Targets = []):
"a dependency of an export must have exactly one output file" "a dependency of an export must have exactly one output file"
) )
emitter_rule(self.name + "+" + destf, srcs, [destf]) subrule = simplerule(
emitter_label(f"CP {destf}") name=self.name + "/+" + destf,
if dir: ins=[srcs[0]],
emitter_exec(["mkdir -p " + dir]) outs=[destf],
commands=["cp %s %s" % (srcs[0], destf)],
emitter_exec(["cp %s %s" % (srcs[0], destf)]) label="CP",
self.outs += [destf] )
subrule.materialise()
emitter_rule(self.name, self.outs, [], deps)
emit("\t@")
if self.outs:
emit("clean::") emit("clean::")
emit("\t$(hide) rm -f " + (" ".join(filenamesof(self.outs)))) emit("\t$(hide) rm -f", destf)
self.outs += deps
emitter_endrule(self.name) self.ins += [subrule]
emitter_rule(
self,
self.ins,
self.outs,
[(d.outs if d.outs else d.sentinel) for d in deps],
)
emitter_endrule(self, self.outs)
def loadbuildfile(filename): def loadbuildfile(filename):
@@ -478,9 +550,11 @@ def main():
loadbuildfile(f) loadbuildfile(f)
for t in flatten([a.split(",") for a in args.targets]): for t in flatten([a.split(",") for a in args.targets]):
if t not in targets: (path, target) = t.split("+", 2)
raise ABException("target %s is not defined" % t) s = join(path, "+" + target)
targets[t].materialise() if s not in targets:
raise ABException("target %s is not defined" % s)
targets[s].materialise()
emit("AB_LOADED = 1\n") emit("AB_LOADED = 1\n")

View File

@@ -1,14 +1,18 @@
from os.path import basename, join from os.path import basename, join
from build.ab import ( from build.ab import (
ABException, ABException,
List,
Rule, Rule,
Targets, Targets,
TargetsMap, TargetsMap,
filenameof, filenameof,
flatten, filenamesmatchingof,
filenamesof, filenamesof,
flatten,
normalrule, normalrule,
bubbledattrsof,
stripext, stripext,
targetswithtraitsof,
) )
from os.path import * from os.path import *
from types import SimpleNamespace from types import SimpleNamespace
@@ -24,7 +28,7 @@ def cfileimpl(self, name, srcs, deps, suffix, commands, label, kind, cflags):
outs=[outleaf], outs=[outleaf],
label=label, label=label,
commands=commands, commands=commands,
cflags=cflags, cflags=cflags + bubbledattrsof(deps, "caller_cflags"),
) )
@@ -32,9 +36,9 @@ def cfileimpl(self, name, srcs, deps, suffix, commands, label, kind, cflags):
def cfile( def cfile(
self, self,
name, name,
srcs: Targets = [], srcs: Targets = None,
deps: Targets = [], deps: Targets = None,
cflags=[], cflags: List = [],
suffix=".o", suffix=".o",
commands=["$(CC) -c -o {outs[0]} {ins[0]} $(CFLAGS) {cflags}"], commands=["$(CC) -c -o {outs[0]} {ins[0]} $(CFLAGS) {cflags}"],
label="CC", label="CC",
@@ -46,9 +50,9 @@ def cfile(
def cxxfile( def cxxfile(
self, self,
name, name,
srcs: Targets = [], srcs: Targets = None,
deps: Targets = [], deps: Targets = None,
cflags=[], cflags: List = [],
suffix=".o", suffix=".o",
commands=["$(CXX) -c -o {outs[0]} {ins[0]} $(CFLAGS) {cflags}"], commands=["$(CXX) -c -o {outs[0]} {ins[0]} $(CFLAGS) {cflags}"],
label="CXX", label="CXX",
@@ -69,7 +73,11 @@ def findsources(name, srcs, deps, cflags, filerule):
cflags=cflags, cflags=cflags,
) )
for f in filenamesof(s) for f in filenamesof(s)
if f.endswith(".c") or f.endswith(".cc") or f.endswith(".cpp") 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] objs += [s]
@@ -77,100 +85,126 @@ def findsources(name, srcs, deps, cflags, filerule):
return objs return objs
def libraryimpl( @Rule
self, name, srcs, deps, hdrs, cflags, ldflags, commands, label, kind def cheaders(
self,
name,
hdrs: TargetsMap = None,
caller_cflags: List = None,
deps: Targets = None,
): ):
if not srcs and not hdrs: cs = []
raise ABException( ins = list(hdrs.values())
"clibrary contains no sources and no exported headers" outs = []
)
libraries = [d for d in deps if hasattr(d, "clibrary")]
for library in libraries:
if library.clibrary.cflags:
cflags += library.clibrary.cflags
if library.clibrary.ldflags:
ldflags += library.clibrary.ldflags
for f in filenamesof(srcs):
if f.endswith(".h"):
deps += [f]
hdrcs = []
hdrins = list(hdrs.values())
hdrouts = []
i = 0 i = 0
for dest, src in hdrs.items(): for dest, src in hdrs.items():
s = filenamesof(src) s = filenamesof(src)
if len(s) != 1: if len(s) != 1:
raise ABException( raise ABException(
"a dependency of an export must have exactly one output file" "the target of a header must return exactly one file"
) )
hdrcs += ["cp {ins[" + str(i) + "]} {outs[" + str(i) + "]}"] cs += ["cp {ins[" + str(i) + "]} {outs[" + str(i) + "]}"]
hdrouts += [dest] outs += [dest]
i = i + 1 i = i + 1
if not hasattr(self, "clibrary"): r = normalrule(
self.clibrary = SimpleNamespace() replaces=self,
if srcs: ins=ins,
hr = None outs=outs,
if hdrcs: commands=cs,
hr = normalrule( deps=deps,
name=f"{name}_hdrs", label="CHEADERS",
ins=hdrins, )
outs=hdrouts, r.materialise()
label="HEADERS", self.attr.caller_cflags = caller_cflags + ["-I" + r.attr.objdir]
commands=hdrcs, self.bubbleattr("caller_cflags", deps)
)
hr.materialise()
actualsrcs = findsources(
name,
srcs,
deps + ([f"{name}_hdrs"] if hr else []),
cflags + ([f"-I{hr.normalrule.objdir}"] if hr else []),
kind,
)
normalrule( def libraryimpl(
self,
name,
srcs,
deps,
hdrs,
caller_cflags,
caller_ldflags,
cflags,
ldflags,
commands,
label,
kind,
):
hr = None
if hdrs and not srcs:
cheaders(
replaces=self, replaces=self,
ins=actualsrcs, hdrs=hdrs,
outs=[basename(name) + ".a"], deps=targetswithtraitsof(deps, "cheaders"),
label=label, caller_cflags=caller_cflags,
commands=commands if actualsrcs else [],
) )
return
self.clibrary.ldflags = ldflags if hdrs:
self.clibrary.cflags = ["-I" + hr.normalrule.objdir] if hr else [] hr = cheaders(
else: name=self.localname + "_hdrs",
r = normalrule( hdrs=hdrs,
replaces=self, deps=targetswithtraitsof(deps, "cheaders"),
ins=hdrins, caller_cflags=caller_cflags,
outs=hdrouts,
label="HEADERS",
commands=hdrcs,
) )
r.materialise() hr.materialise()
deps = deps + [hr]
self.clibrary.ldflags = ldflags objs = findsources(
self.clibrary.cflags = ["-I" + r.normalrule.objdir] name,
srcs,
targetswithtraitsof(deps, "cheaders"),
cflags + bubbledattrsof(deps, "caller_cflags"),
kind,
)
normalrule(
replaces=self,
ins=objs,
outs=[basename(name) + ".a"],
label=label,
commands=commands,
)
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 @Rule
def clibrary( def clibrary(
self, self,
name, name,
srcs: Targets = [], srcs: Targets = None,
deps: Targets = [], deps: Targets = None,
hdrs: TargetsMap = {}, hdrs: TargetsMap = None,
cflags=[], caller_cflags: List = [],
ldflags=[], caller_ldflags: List = [],
cflags: List = [],
ldflags: List = [],
commands=["$(AR) cqs {outs[0]} {ins}"], commands=["$(AR) cqs {outs[0]} {ins}"],
label="LIB", label="LIB",
cfilerule=cfile,
): ):
return libraryimpl( libraryimpl(
self, name, srcs, deps, hdrs, cflags, ldflags, commands, label, cfile self,
name,
srcs,
deps,
hdrs,
caller_cflags,
caller_ldflags,
cflags,
ldflags,
commands,
label,
cfilerule,
) )
@@ -178,35 +212,43 @@ def clibrary(
def cxxlibrary( def cxxlibrary(
self, self,
name, name,
srcs: Targets = [], srcs: Targets = None,
deps: Targets = [], deps: Targets = None,
hdrs: TargetsMap = {}, hdrs: TargetsMap = None,
cflags=[], caller_cflags: List = [],
ldflags=[], caller_ldflags: List = [],
cflags: List = [],
ldflags: List = [],
commands=["$(AR) cqs {outs[0]} {ins}"], commands=["$(AR) cqs {outs[0]} {ins}"],
label="LIB", label="LIB",
): ):
return libraryimpl( libraryimpl(
self, name, srcs, deps, hdrs, cflags, ldflags, commands, label, cxxfile self,
name,
srcs,
deps,
hdrs,
caller_cflags,
caller_ldflags,
cflags,
ldflags,
commands,
label,
cxxfile,
) )
def programimpl( def programimpl(
self, name, srcs, deps, cflags, ldflags, commands, label, filerule, kind self, name, srcs, deps, cflags, ldflags, commands, label, filerule, kind
): ):
libraries = [d for d in deps if hasattr(d, "clibrary")] ars = filenamesmatchingof(deps, "*.a")
for library in libraries: deps = deps + filenamesmatchingof(srcs, "*.h")
if library.clibrary.cflags: ldflags = ldflags + bubbledattrsof(deps, "caller_ldflags")
cflags += library.clibrary.cflags
if library.clibrary.ldflags:
ldflags += library.clibrary.ldflags
deps += [f for f in filenamesof(srcs) if f.endswith(".h")] cfiles = findsources(name, srcs, deps, cflags, filerule)
ars = [f for f in filenamesof(libraries) if f.endswith(".a")]
normalrule( normalrule(
replaces=self, replaces=self,
ins=(findsources(name, srcs, deps, cflags, filerule) + ars + ars), ins=cfiles + ars + ars,
outs=[basename(name) + "$(EXT)"], outs=[basename(name) + "$(EXT)"],
deps=deps, deps=deps,
label=label, label=label,
@@ -219,12 +261,14 @@ def programimpl(
def cprogram( def cprogram(
self, self,
name, name,
srcs: Targets = [], srcs: Targets = None,
deps: Targets = [], deps: Targets = None,
cflags=[], cflags: List = [],
ldflags=[], ldflags: List = [],
commands=["$(CC) -o {outs[0]} {ins} {ldflags} $(LDFLAGS)"], commands=["$(CC) -o {outs[0]} {ins} {ldflags} $(LDFLAGS)"],
label="CLINK", label="CLINK",
cfilerule=cfile,
cfilekind="cprogram",
): ):
programimpl( programimpl(
self, self,
@@ -235,8 +279,8 @@ def cprogram(
ldflags, ldflags,
commands, commands,
label, label,
cfile, cfilerule,
"cprogram", cfilekind,
) )
@@ -244,10 +288,10 @@ def cprogram(
def cxxprogram( def cxxprogram(
self, self,
name, name,
srcs: Targets = [], srcs: Targets = None,
deps: Targets = [], deps: Targets = None,
cflags=[], cflags: List = [],
ldflags=[], ldflags: List = [],
commands=["$(CXX) -o {outs[0]} {ins} {ldflags} $(LDFLAGS)"], commands=["$(CXX) -o {outs[0]} {ins} {ldflags} $(LDFLAGS)"],
label="CXXLINK", label="CXXLINK",
): ):

View File

@@ -1,4 +1,4 @@
from build.ab import Rule, emit, Target from build.ab import Rule, emit, Target, bubbledattrsof
from types import SimpleNamespace from types import SimpleNamespace
import os import os
import subprocess import subprocess
@@ -15,8 +15,14 @@ PACKAGES := $(shell $(PKG_CONFIG) --list-all | cut -d' ' -f1 | sort)
def package(self, name, package=None, fallback: Target = None): def package(self, name, package=None, fallback: Target = None):
emit("ifeq ($(filter %s, $(PACKAGES)),)" % package) emit("ifeq ($(filter %s, $(PACKAGES)),)" % package)
if fallback: if fallback:
emit(f"PACKAGE_CFLAGS_{package} :=", fallback.clibrary.cflags) emit(
emit(f"PACKAGE_LDFLAGS_{package} := ", fallback.clibrary.ldflags) f"PACKAGE_CFLAGS_{package} :=",
bubbledattrsof(fallback, "caller_cflags"),
)
emit(
f"PACKAGE_LDFLAGS_{package} := ",
bubbledattrsof(fallback, "caller_ldflags"),
)
emit(f"PACKAGE_DEP_{package} := ", fallback.name) emit(f"PACKAGE_DEP_{package} := ", fallback.name)
else: else:
emit(f"$(error Required package '{package}' not installed.)") emit(f"$(error Required package '{package}' not installed.)")
@@ -30,9 +36,8 @@ def package(self, name, package=None, fallback: Target = None):
emit(f"PACKAGE_DEP_{package} := ") emit(f"PACKAGE_DEP_{package} := ")
emit("endif") emit("endif")
self.clibrary = SimpleNamespace() self.attr.caller_cflags = [f"$(PACKAGE_CFLAGS_{package})"]
self.clibrary.cflags = [f"$(PACKAGE_CFLAGS_{package})"] self.attr.caller_ldflags = [f"$(PACKAGE_LDFLAGS_{package})"]
self.clibrary.ldflags = [f"$(PACKAGE_LDFLAGS_{package})"]
self.ins = [] self.ins = []
self.outs = [f"$(PACKAGE_DEP_{package})"] self.outs = [f"$(PACKAGE_DEP_{package})"]

View File

@@ -1,8 +1,17 @@
from os.path import join from os.path import join
from build.ab import Rule, Targets, emit, normalrule, filenamesof, flatten from build.ab import (
Rule,
Targets,
emit,
normalrule,
filenamesof,
filenamesmatchingof,
bubbledattrsof,
targetswithtraitsof,
)
from build.c import cxxlibrary from build.c import cxxlibrary
import build.pkg
from types import SimpleNamespace from types import SimpleNamespace
import build.pkg
emit( emit(
""" """
@@ -15,7 +24,7 @@ endif
@Rule @Rule
def proto(self, name, srcs: Targets = [], deps: Targets = []): def proto(self, name, srcs: Targets = None, deps: Targets = None):
normalrule( normalrule(
replaces=self, replaces=self,
ins=srcs, ins=srcs,
@@ -26,40 +35,38 @@ def proto(self, name, srcs: Targets = [], deps: Targets = []):
], ],
label="PROTO", label="PROTO",
) )
self.proto.srcs = filenamesof(srcs) + flatten( self.attr.protosrcs = filenamesof(srcs)
[s.proto.srcs for s in flatten(deps)] self.bubbleattr("protosrcs", deps)
)
@Rule @Rule
def protocc(self, name, srcs: Targets = [], deps: Targets = []): def protocc(self, name, srcs: Targets = None, deps: Targets = None):
outs = [] outs = []
protos = [] protos = []
for f in flatten([s.proto.srcs for s in flatten(srcs + deps)]):
if f.endswith(".proto"):
cc = f.replace(".proto", ".pb.cc")
h = f.replace(".proto", ".pb.h")
protos += [f]
srcs += [f]
outs += [cc, h]
for f in filenamesmatchingof(bubbledattrsof(srcs, "protosrcs"), "*.proto"):
cc = f.replace(".proto", ".pb.cc")
h = f.replace(".proto", ".pb.h")
protos += [f]
srcs += [f]
outs += [cc, h]
srcname = f"{name}_srcs"
objdir = join("$(OBJ)", srcname)
r = normalrule( r = normalrule(
name=f"{name}_srcs", name=srcname,
ins=protos, ins=protos,
outs=outs, outs=outs,
deps=deps, deps=deps,
commands=["$(PROTOC) --cpp_out={self.normalrule.objdir} {ins}"], commands=["$(PROTOC) --cpp_out={self.attr.objdir} {ins}"],
label="PROTOCC", label="PROTOCC",
) )
r.materialise() headers = {f: join(objdir, f) for f in outs if f.endswith(".pb.h")}
headers = {
f: join(r.normalrule.objdir, f) for f in outs if f.endswith(".pb.h")
}
cxxlibrary( cxxlibrary(
replaces=self, replaces=self,
srcs=[f"{name}_srcs"], srcs=[r],
deps=targetswithtraitsof(deps, "cheaders"),
hdrs=headers, hdrs=headers,
cflags=[f"-I{r.normalrule.objdir}"],
) )

View File

@@ -19,8 +19,8 @@ def test(
name, name,
command: Target = None, command: Target = None,
commands=None, commands=None,
ins: Targets = [], ins: Targets = None,
deps: Targets = [], deps: Targets = None,
label="TEST", label="TEST",
): ):
if command: if command:

View File

@@ -22,4 +22,4 @@ proto(
deps=[".+common_proto", "+fl2_proto"], deps=[".+common_proto", "+fl2_proto"],
) )
protocc(name="config_proto_lib", srcs=[".+config_proto", "arch+arch_proto"]) protocc(name="config_proto_lib", srcs=[".+common_proto", ".+config_proto", "arch+arch_proto"])

View File

@@ -16,6 +16,7 @@ def protoencode(self, name, srcs: Targets, proto, symbol):
"tests+test_proto_lib", "tests+test_proto_lib",
"+protobuf_lib", "+protobuf_lib",
"+fmt_lib", "+fmt_lib",
"+lib",
], ],
) )
encoders[proto] = r encoders[proto] = r
@@ -36,11 +37,11 @@ def protoencode(self, name, srcs: Targets, proto, symbol):
cxxprogram( cxxprogram(
name="mkdoc", name="mkdoc",
srcs=["./mkdoc.cc"], srcs=["./mkdoc.cc"],
deps=["src/formats", "lib+config_proto_lib", "+lib"], deps=["src/formats", "lib+config_proto_lib", "+lib", "+fmt_lib", "+protobuf_lib"],
) )
cxxprogram( cxxprogram(
name="mkdocindex", name="mkdocindex",
srcs=["./mkdocindex.cc"], srcs=["./mkdocindex.cc"],
deps=["src/formats", "lib+config_proto_lib", "+lib"], deps=["src/formats", "lib+config_proto_lib", "+lib", "+fmt_lib", "+protobuf_lib"],
) )

View File

@@ -24,7 +24,7 @@ if config.windows:
outs=["rc.o"], outs=["rc.o"],
deps=["./manifest.xml", "extras+fluxengine_ico"], deps=["./manifest.xml", "extras+fluxengine_ico"],
commands=["$(WINDRES) {ins[0]} {outs[0]}"], commands=["$(WINDRES) {ins[0]} {outs[0]}"],
label="WINDRES" label="WINDRES",
) )
] ]

View File

@@ -31,4 +31,4 @@ encoded = [
for name in drivetypes for name in drivetypes
] ]
cxxlibrary(name="drivetypes", srcs=[".+drivetypes_cc"] + encoded) cxxlibrary(name="drivetypes", srcs=[".+drivetypes_cc"] + encoded, deps=["+lib"])

View File

@@ -13,9 +13,7 @@ proto(
) )
protocc( protocc(
name="test_proto_lib", name="test_proto_lib", srcs=[".+test_proto"], deps=["lib+config_proto_lib"]
srcs=[".+test_proto"],
deps=["lib+config_proto", "arch+arch_proto"],
) )
tests = [ tests = [
@@ -60,6 +58,9 @@ export(
], ],
deps=[ deps=[
"+fl2_proto_lib", "+fl2_proto_lib",
"+fmt_lib",
"+lib",
"+protobuf_lib",
"+protocol", "+protocol",
"+z_lib", "+z_lib",
".+test_proto_lib", ".+test_proto_lib",
@@ -70,7 +71,6 @@ export(
"dep/libusbp", "dep/libusbp",
"dep/snowhouse", "dep/snowhouse",
"dep/stb", "dep/stb",
"+lib",
"lib+config_proto_lib", "lib+config_proto_lib",
"src/formats", "src/formats",
], ],
@@ -85,7 +85,11 @@ export(
srcs=[f"./{n}.cc"], srcs=[f"./{n}.cc"],
deps=[ deps=[
"+fl2_proto_lib", "+fl2_proto_lib",
"+fmt_lib",
"+lib",
"+protobuf_lib",
"+protocol", "+protocol",
"+z_lib",
"dep/adflib", "dep/adflib",
"dep/agg", "dep/agg",
"dep/fatfs", "dep/fatfs",
@@ -93,10 +97,10 @@ export(
"dep/libusbp", "dep/libusbp",
"dep/snowhouse", "dep/snowhouse",
"dep/stb", "dep/stb",
"+lib",
"lib+config_proto_lib", "lib+config_proto_lib",
"src/formats", "src/formats",
], ]
+ ([".+test_proto_lib"] if n == "options" else []),
), ),
) )
for n in tests for n in tests

View File

@@ -8,25 +8,28 @@ if config.windows:
cxxprogram( cxxprogram(
name="brother120tool", name="brother120tool",
srcs=["./brother120tool.cc"], srcs=["./brother120tool.cc"],
deps=["+lib", "lib+config_proto_lib"] + emu, deps=["+lib", "lib+config_proto_lib", "+fmt_lib", "+z_lib"] + emu,
) )
cxxprogram( cxxprogram(
name="brother240tool", name="brother240tool",
srcs=["./brother240tool.cc"], srcs=["./brother240tool.cc"],
deps=["+lib", "lib+config_proto_lib"] + emu, deps=["+lib", "lib+config_proto_lib", "+fmt_lib", "+z_lib"] + emu,
) )
cxxprogram( cxxprogram(
name="upgrade-flux-file", name="upgrade-flux-file",
srcs=["./upgrade-flux-file.cc"], srcs=["./upgrade-flux-file.cc"],
deps=[ deps=[
"+lib",
"src/formats",
"lib+config_proto_lib",
"+protocol",
"+fl2_proto_lib", "+fl2_proto_lib",
"+fmt_lib",
"+lib",
"+protobuf_lib",
"+protocol",
"+sqlite3_lib", "+sqlite3_lib",
"+z_lib",
"dep/libusbp", "dep/libusbp",
"lib+config_proto_lib",
"src/formats",
], ],
) )