mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Update to the new ninja-fied ab.
This commit is contained in:
3
Makefile
3
Makefile
@@ -26,7 +26,8 @@ ifeq ($(BUILDTYPE),windows)
|
|||||||
else
|
else
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CXX = g++ -std=c++20
|
CXX = g++ -std=c++20
|
||||||
CFLAGS = -g -O3 \
|
CFLAGS = -g -O3
|
||||||
|
CXXFLAGS += \
|
||||||
-Wno-deprecated-enum-float-conversion \
|
-Wno-deprecated-enum-float-conversion \
|
||||||
-Wno-deprecated-enum-enum-conversion
|
-Wno-deprecated-enum-enum-conversion
|
||||||
LDFLAGS =
|
LDFLAGS =
|
||||||
|
|||||||
4
build.py
4
build.py
@@ -8,7 +8,7 @@ import config
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
# Hack for building on Fedora/WSL; executables get the .exe extension,
|
# Hack for building on Fedora/WSL; executables get the .exe extension,
|
||||||
# build the build system detects it as Linux.
|
# but the build system detects it as Linux.
|
||||||
import build.toolchain
|
import build.toolchain
|
||||||
|
|
||||||
toolchain.Toolchain.EXE = "$(EXT)"
|
toolchain.Toolchain.EXE = "$(EXT)"
|
||||||
@@ -93,7 +93,7 @@ else:
|
|||||||
+ c[1]
|
+ c[1]
|
||||||
+ "' '"
|
+ "' '"
|
||||||
+ c[2]
|
+ c[2]
|
||||||
+ "' $(dir $[outs[0]]) > /dev/null"
|
+ "' $[dirname(filenameof(outs[0]))] > /dev/null"
|
||||||
],
|
],
|
||||||
label="CORPUSTEST",
|
label="CORPUSTEST",
|
||||||
)
|
)
|
||||||
|
|||||||
69
build/ab.mk
69
build/ab.mk
@@ -15,16 +15,17 @@ HOSTCC ?= gcc
|
|||||||
HOSTCXX ?= g++
|
HOSTCXX ?= g++
|
||||||
HOSTAR ?= ar
|
HOSTAR ?= ar
|
||||||
HOSTCFLAGS ?= -g -Og
|
HOSTCFLAGS ?= -g -Og
|
||||||
|
HOSTCXXFLAGS ?= $(HOSTCFLAGS)
|
||||||
HOSTLDFLAGS ?= -g
|
HOSTLDFLAGS ?= -g
|
||||||
|
|
||||||
CC ?= $(HOSTCC)
|
CC ?= $(HOSTCC)
|
||||||
CXX ?= $(HOSTCXX)
|
CXX ?= $(HOSTCXX)
|
||||||
AR ?= $(HOSTAR)
|
AR ?= $(HOSTAR)
|
||||||
CFLAGS ?= $(HOSTCFLAGS)
|
CFLAGS ?= $(HOSTCFLAGS)
|
||||||
|
CXXFLAGS ?= $(CFLAGS)
|
||||||
LDFLAGS ?= $(HOSTLDFLAGS)
|
LDFLAGS ?= $(HOSTLDFLAGS)
|
||||||
|
|
||||||
export PKG_CONFIG
|
NINJA ?= ninja
|
||||||
export HOST_PKG_CONFIG
|
|
||||||
|
|
||||||
ifdef VERBOSE
|
ifdef VERBOSE
|
||||||
hide =
|
hide =
|
||||||
@@ -63,37 +64,33 @@ EXT ?=
|
|||||||
|
|
||||||
CWD=$(shell pwd)
|
CWD=$(shell pwd)
|
||||||
|
|
||||||
ifeq ($(AB_ENABLE_PROGRESS_INFO),true)
|
define newline
|
||||||
ifeq ($(PROGRESSINFO),)
|
|
||||||
# The first make invocation here has to have its output discarded or else it
|
|
||||||
# produces spurious 'Leaving directory' messages... don't know why.
|
|
||||||
rulecount := $(strip $(shell $(MAKE) --no-print-directory -q $(OBJ)/build.mk PROGRESSINFO=1 > /dev/null \
|
|
||||||
&& $(MAKE) --no-print-directory -n $(MAKECMDGOALS) PROGRESSINFO=XXXPROGRESSINFOXXX | grep XXXPROGRESSINFOXXX | wc -l))
|
|
||||||
ruleindex := 1
|
|
||||||
PROGRESSINFO = "[$(ruleindex)/$(rulecount)]$(eval ruleindex := $(shell expr $(ruleindex) + 1)) "
|
|
||||||
endif
|
|
||||||
else
|
|
||||||
PROGRESSINFO = ""
|
|
||||||
endif
|
|
||||||
|
|
||||||
PKG_CONFIG_HASHES = $(OBJ)/.pkg-config-hashes/target-$(word 1, $(shell $(PKG_CONFIG) --list-all | md5sum))
|
|
||||||
HOST_PKG_CONFIG_HASHES = $(OBJ)/.pkg-config-hashes/host-$(word 1, $(shell $(HOST_PKG_CONFIG) --list-all | md5sum))
|
|
||||||
|
|
||||||
$(OBJ)/build.mk : $(PKG_CONFIG_HASHES) $(HOST_PKG_CONFIG_HASHES)
|
endef
|
||||||
$(PKG_CONFIG_HASHES) $(HOST_PKG_CONFIG_HASHES) &:
|
|
||||||
$(hide) rm -rf $(OBJ)/.pkg-config-hashes
|
|
||||||
$(hide) mkdir -p $(OBJ)/.pkg-config-hashes
|
|
||||||
$(hide) touch $(PKG_CONFIG_HASHES) $(HOST_PKG_CONFIG_HASHES)
|
|
||||||
|
|
||||||
include $(OBJ)/build.mk
|
define check_for_command
|
||||||
|
$(shell command -v $1 >/dev/null || (echo "Required command '$1' missing" >/dev/stderr && kill $$PPID))
|
||||||
|
endef
|
||||||
|
|
||||||
ifeq ($(OSX),yes)
|
$(call check_for_command,ninja)
|
||||||
MAKEFLAGS += -r -j$(shell sysctl -n hw.logicalcpu)
|
$(call check_for_command,cmp)
|
||||||
else
|
$(call check_for_command,$(PYTHON))
|
||||||
MAKEFLAGS += -r -j$(shell nproc)
|
|
||||||
endif
|
|
||||||
|
|
||||||
.DELETE_ON_ERROR:
|
pkg-config-hash = $(shell ($(PKG_CONFIG) --list-all && $(HOST_PKG_CONFIG) --list-all) | md5sum)
|
||||||
|
build-files = $(shell find . -name .obj -prune -o \( -name 'build.py' -a -type f \) -print) $(wildcard build/*.py) $(wildcard config.py)
|
||||||
|
build-file-timestamps = $(shell ls -l $(build-files) | md5sum)
|
||||||
|
|
||||||
|
# Wipe the build file (forcing a regeneration) if the make environment is different.
|
||||||
|
# (Conveniently, this includes the pkg-config hash calculated above.)
|
||||||
|
|
||||||
|
ignored-variables = MAKE_RESTARTS .VARIABLES MAKECMDGOALS MAKEFLAGS MFLAGS
|
||||||
|
$(shell mkdir -p $(OBJ))
|
||||||
|
$(file >$(OBJ)/newvars.txt,$(foreach v,$(filter-out $(ignored-variables),$(.VARIABLES)),$(v)=$($(v))$(newline)))
|
||||||
|
$(shell touch $(OBJ)/vars.txt)
|
||||||
|
#$(shell diff -u $(OBJ)/vars.txt $(OBJ)/newvars.txt > /dev/stderr)
|
||||||
|
$(shell cmp -s $(OBJ)/newvars.txt $(OBJ)/vars.txt || (rm -f $(OBJ)/build.ninja && echo "Environment changed --- regenerating" > /dev/stderr))
|
||||||
|
$(shell mv $(OBJ)/newvars.txt $(OBJ)/vars.txt)
|
||||||
|
|
||||||
.PHONY: update-ab
|
.PHONY: update-ab
|
||||||
update-ab:
|
update-ab:
|
||||||
@@ -108,9 +105,15 @@ clean::
|
|||||||
$(hide) rm -rf $(OBJ)
|
$(hide) rm -rf $(OBJ)
|
||||||
|
|
||||||
export PYTHONHASHSEED = 1
|
export PYTHONHASHSEED = 1
|
||||||
build-files = $(shell find . -name 'build.py') $(wildcard build/*.py) $(wildcard config.py)
|
$(OBJ)/build.ninja $(OBJ)/build.targets &:
|
||||||
$(OBJ)/build.mk: Makefile $(build-files) build/ab.mk
|
|
||||||
@echo "AB"
|
@echo "AB"
|
||||||
@mkdir -p $(OBJ)
|
$(hide) $(PYTHON) -X pycache_prefix=$(OBJ)/__pycache__ build/ab.py \
|
||||||
$(hide) $(PYTHON) -X pycache_prefix=$(OBJ)/__pycache__ build/ab.py -o $@ build.py \
|
-o $(OBJ) build.py \
|
||||||
|| rm -f $@
|
-v $(OBJ)/vars.txt \
|
||||||
|
|| (rm -f $@ && false)
|
||||||
|
|
||||||
|
include $(OBJ)/build.targets
|
||||||
|
.PHONY: $(ninja-targets)
|
||||||
|
$(ninja-targets) &: $(OBJ)/build.ninja
|
||||||
|
@echo "NINJA"
|
||||||
|
+$(hide) $(NINJA) -f $(OBJ)/build.ninja $@
|
||||||
|
|||||||
244
build/ab.py
244
build/ab.py
@@ -1,36 +1,32 @@
|
|||||||
|
from collections import namedtuple
|
||||||
|
from copy import copy
|
||||||
|
from importlib.machinery import SourceFileLoader, PathFinder, ModuleSpec
|
||||||
from os.path import *
|
from os.path import *
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
import argparse
|
import argparse
|
||||||
|
import ast
|
||||||
import builtins
|
import builtins
|
||||||
from copy import copy
|
|
||||||
import functools
|
import functools
|
||||||
|
import hashlib
|
||||||
import importlib
|
import importlib
|
||||||
import importlib.util
|
import importlib.util
|
||||||
from importlib.machinery import (
|
|
||||||
SourceFileLoader,
|
|
||||||
PathFinder,
|
|
||||||
ModuleSpec,
|
|
||||||
)
|
|
||||||
import inspect
|
import inspect
|
||||||
|
import os
|
||||||
|
import re
|
||||||
import string
|
import string
|
||||||
import sys
|
import sys
|
||||||
import hashlib
|
import types
|
||||||
import re
|
|
||||||
import ast
|
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
VERBOSE_MK_FILE = False
|
VERBOSE_NINJA_FILE = False
|
||||||
|
|
||||||
verbose = False
|
|
||||||
quiet = False
|
quiet = False
|
||||||
cwdStack = [""]
|
cwdStack = [""]
|
||||||
targets = {}
|
targets = {}
|
||||||
unmaterialisedTargets = {} # dict, not set, to get consistent ordering
|
unmaterialisedTargets = {} # dict, not set, to get consistent ordering
|
||||||
materialisingStack = []
|
materialisingStack = []
|
||||||
defaultGlobals = {}
|
defaultGlobals = {}
|
||||||
globalId = 1
|
outputTargets = set()
|
||||||
wordCache = {}
|
|
||||||
|
|
||||||
RE_FORMAT_SPEC = re.compile(
|
RE_FORMAT_SPEC = re.compile(
|
||||||
r"(?:(?P<fill>[\s\S])?(?P<align>[<>=^]))?"
|
r"(?:(?P<fill>[\s\S])?(?P<align>[<>=^]))?"
|
||||||
@@ -52,6 +48,15 @@ sys.path += ["."]
|
|||||||
old_import = builtins.__import__
|
old_import = builtins.__import__
|
||||||
|
|
||||||
|
|
||||||
|
class Environment(types.SimpleNamespace):
|
||||||
|
def setdefault(self, name, value):
|
||||||
|
if not hasattr(self, name):
|
||||||
|
setattr(self, name, value)
|
||||||
|
|
||||||
|
|
||||||
|
G = Environment()
|
||||||
|
|
||||||
|
|
||||||
class PathFinderImpl(PathFinder):
|
class PathFinderImpl(PathFinder):
|
||||||
def find_spec(self, fullname, path, target=None):
|
def find_spec(self, fullname, path, target=None):
|
||||||
# The second test here is needed for Python 3.9.
|
# The second test here is needed for Python 3.9.
|
||||||
@@ -103,26 +108,71 @@ def error(message):
|
|||||||
|
|
||||||
|
|
||||||
class BracketedFormatter(string.Formatter):
|
class BracketedFormatter(string.Formatter):
|
||||||
|
def __init__(self, op, cl):
|
||||||
|
self.op = op
|
||||||
|
self.cl = cl
|
||||||
|
|
||||||
|
def _undo_escaped_dollar(self, s):
|
||||||
|
return s.replace(f"$${self.op}", f"${self.op}")
|
||||||
|
|
||||||
def parse(self, format_string):
|
def parse(self, format_string):
|
||||||
while format_string:
|
while format_string:
|
||||||
left, *right = format_string.split("$[", 1)
|
m = re.search(f"(?:[^$]|^)()\\$\\{self.op}()", format_string)
|
||||||
if not right:
|
if not m:
|
||||||
yield (left, None, None, None)
|
yield (
|
||||||
|
self._undo_escaped_dollar(format_string),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
break
|
break
|
||||||
right = right[0]
|
left = format_string[: m.start(1)]
|
||||||
|
right = format_string[m.end(2) :]
|
||||||
|
|
||||||
offset = len(right) + 1
|
offset = len(right) + 1
|
||||||
try:
|
try:
|
||||||
ast.parse(right)
|
ast.parse(right)
|
||||||
except SyntaxError as e:
|
except SyntaxError as e:
|
||||||
if not str(e).startswith("unmatched ']'"):
|
if not str(e).startswith(f"unmatched '{self.cl}'"):
|
||||||
raise e
|
raise e
|
||||||
offset = e.offset
|
offset = e.offset
|
||||||
|
|
||||||
expr = right[0 : offset - 1]
|
expr = right[0 : offset - 1]
|
||||||
format_string = right[offset:]
|
format_string = right[offset:]
|
||||||
|
|
||||||
yield (left if left else None, expr, None, None)
|
yield (
|
||||||
|
self._undo_escaped_dollar(left) if left else None,
|
||||||
|
expr,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GlobalFormatter(BracketedFormatter):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("(", ")")
|
||||||
|
|
||||||
|
def get_field(self, name, a1, a2):
|
||||||
|
return (
|
||||||
|
getattr(G, name),
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def format_field(self, value, format_spec):
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
|
globalFormatter = GlobalFormatter()
|
||||||
|
|
||||||
|
|
||||||
|
def substituteGlobalVariables(value):
|
||||||
|
while True:
|
||||||
|
oldValue = value
|
||||||
|
value = globalFormatter.format(value)
|
||||||
|
if value == oldValue:
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def Rule(func):
|
def Rule(func):
|
||||||
@@ -187,12 +237,10 @@ def _isiterable(xs):
|
|||||||
|
|
||||||
class Target:
|
class Target:
|
||||||
def __init__(self, cwd, name):
|
def __init__(self, cwd, name):
|
||||||
if verbose:
|
|
||||||
print("rule('%s', cwd='%s'" % (name, cwd))
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.localname = self.name.rsplit("+")[-1]
|
self.localname = self.name.rsplit("+")[-1]
|
||||||
self.traits = set()
|
self.traits = set()
|
||||||
self.dir = join("$(OBJ)", name)
|
self.dir = join(G.OBJ, name)
|
||||||
self.ins = []
|
self.ins = []
|
||||||
self.outs = []
|
self.outs = []
|
||||||
self.deps = []
|
self.deps = []
|
||||||
@@ -213,6 +261,9 @@ class Target:
|
|||||||
|
|
||||||
def templateexpand(selfi, s):
|
def templateexpand(selfi, s):
|
||||||
class Formatter(BracketedFormatter):
|
class Formatter(BracketedFormatter):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("[", "]")
|
||||||
|
|
||||||
def get_field(self, name, a1, a2):
|
def get_field(self, name, a1, a2):
|
||||||
return (
|
return (
|
||||||
eval(name, selfi.callback.__globals__, selfi.args),
|
eval(name, selfi.callback.__globals__, selfi.args),
|
||||||
@@ -232,7 +283,8 @@ class Target:
|
|||||||
[selfi.templateexpand(f) for f in filenamesof(value)]
|
[selfi.templateexpand(f) for f in filenamesof(value)]
|
||||||
)
|
)
|
||||||
|
|
||||||
return Formatter().format(s)
|
s = Formatter().format(s)
|
||||||
|
return substituteGlobalVariables(s)
|
||||||
|
|
||||||
def materialise(self, replacing=False):
|
def materialise(self, replacing=False):
|
||||||
if self not in unmaterialisedTargets:
|
if self not in unmaterialisedTargets:
|
||||||
@@ -341,10 +393,10 @@ def targetof(value, cwd=None):
|
|||||||
elif value.startswith("./"):
|
elif value.startswith("./"):
|
||||||
value = normpath(join(cwd, value))
|
value = normpath(join(cwd, value))
|
||||||
# Explicit directories are always raw files.
|
# Explicit directories are always raw files.
|
||||||
elif value.endswith("/"):
|
if value.endswith("/"):
|
||||||
return _filetarget(value, cwd)
|
return _filetarget(value, cwd)
|
||||||
# Anything starting with a variable expansion is always a raw file.
|
# Anything in .obj is a raw file.
|
||||||
elif value.startswith("$"):
|
elif value.startswith(outputdir) or value.startswith(G.OBJ):
|
||||||
return _filetarget(value, cwd)
|
return _filetarget(value, cwd)
|
||||||
|
|
||||||
# If this is not a rule lookup...
|
# If this is not a rule lookup...
|
||||||
@@ -467,78 +519,67 @@ def emit(*args, into=None):
|
|||||||
if into is not None:
|
if into is not None:
|
||||||
into += [s]
|
into += [s]
|
||||||
else:
|
else:
|
||||||
outputFp.write(s)
|
ninjaFp.write(s)
|
||||||
|
|
||||||
|
|
||||||
|
def shell(*args):
|
||||||
|
s = "".join(args) + "\n"
|
||||||
|
shellFp.write(s)
|
||||||
|
|
||||||
|
|
||||||
def emit_rule(self, ins, outs, cmds=[], label=None):
|
def emit_rule(self, ins, outs, cmds=[], label=None):
|
||||||
name = self.name
|
name = self.name
|
||||||
fins_list = filenamesof(ins)
|
fins = [self.templateexpand(f) for f in set(filenamesof(ins))]
|
||||||
fins = set(fins_list)
|
fouts = [self.templateexpand(f) for f in filenamesof(outs)]
|
||||||
fouts = filenamesof(outs)
|
|
||||||
nonobjs = [f for f in fouts if not f.startswith("$(OBJ)")]
|
global outputTargets
|
||||||
|
outputTargets.update(fouts)
|
||||||
|
outputTargets.add(name)
|
||||||
|
|
||||||
emit("")
|
emit("")
|
||||||
if VERBOSE_MK_FILE:
|
if VERBOSE_NINJA_FILE:
|
||||||
for k, v in self.args.items():
|
for k, v in self.args.items():
|
||||||
emit(f"# {k} = {v}")
|
emit(f"# {k} = {v}")
|
||||||
|
|
||||||
lines = []
|
|
||||||
if nonobjs:
|
|
||||||
emit("clean::", into=lines)
|
|
||||||
emit("\t$(hide) rm -f", *nonobjs, into=lines)
|
|
||||||
|
|
||||||
hashable = cmds + fins_list + fouts
|
|
||||||
hash = hashlib.sha1(bytes("\n".join(hashable), "utf-8")).hexdigest()
|
|
||||||
hashfile = join(self.dir, f"hash_{hash}")
|
|
||||||
|
|
||||||
global globalId
|
|
||||||
emit(".PHONY:", name, into=lines)
|
|
||||||
if outs:
|
if outs:
|
||||||
outsn = globalId
|
os.makedirs(self.dir, exist_ok=True)
|
||||||
globalId = globalId + 1
|
rule = []
|
||||||
insn = globalId
|
|
||||||
globalId = globalId + 1
|
|
||||||
|
|
||||||
emit(f"OUTS_{outsn}", "=", *fouts, into=lines)
|
|
||||||
emit(f"INS_{insn}", "=", *fins, into=lines)
|
|
||||||
emit(name, ":", f"$(OUTS_{outsn})", into=lines)
|
|
||||||
emit(hashfile, ":", into=lines)
|
|
||||||
emit(f"\t@mkdir -p {self.dir}", into=lines)
|
|
||||||
emit(f"\t@touch {hashfile}", into=lines)
|
|
||||||
emit(
|
|
||||||
f"$(OUTS_{outsn})",
|
|
||||||
"&:" if len(fouts) > 1 else ":",
|
|
||||||
f"$(INS_{insn})",
|
|
||||||
hashfile,
|
|
||||||
into=lines,
|
|
||||||
)
|
|
||||||
|
|
||||||
if label:
|
|
||||||
emit("\t$(hide)", "$(ECHO) $(PROGRESSINFO)" + label, into=lines)
|
|
||||||
|
|
||||||
sandbox = join(self.dir, "sandbox")
|
sandbox = join(self.dir, "sandbox")
|
||||||
emit("\t$(hide)", f"rm -rf {sandbox}", into=lines)
|
emit(f"rm -rf {sandbox}", into=rule)
|
||||||
emit(
|
emit(
|
||||||
"\t$(hide)",
|
f"{G.PYTHON} build/_sandbox.py --link -s", sandbox, *fins, into=rule
|
||||||
"$(PYTHON) build/_sandbox.py --link -s",
|
|
||||||
sandbox,
|
|
||||||
f"$(INS_{insn})",
|
|
||||||
into=lines,
|
|
||||||
)
|
)
|
||||||
for c in cmds:
|
for c in cmds:
|
||||||
emit(f"\t$(hide) cd {sandbox} && (", c, ")", into=lines)
|
emit(f"(cd {sandbox} &&", c, ")", into=rule)
|
||||||
emit(
|
emit(
|
||||||
"\t$(hide)",
|
f"{G.PYTHON} build/_sandbox.py --export -s",
|
||||||
"$(PYTHON) build/_sandbox.py --export -s",
|
|
||||||
sandbox,
|
sandbox,
|
||||||
f"$(OUTS_{outsn})",
|
*fouts,
|
||||||
into=lines,
|
into=rule,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ruletext = "".join(rule)
|
||||||
|
if len(ruletext) > 7000:
|
||||||
|
rulehash = hashlib.sha1(ruletext.encode()).hexdigest()
|
||||||
|
|
||||||
|
rulef = join(self.dir, f"rule-{rulehash}.sh")
|
||||||
|
with open(rulef, "wt") as fp:
|
||||||
|
fp.write(ruletext)
|
||||||
|
|
||||||
|
emit("build", *fouts, ":rule", *fins, rulef)
|
||||||
|
emit(" command=sh", rulef)
|
||||||
|
else:
|
||||||
|
emit("build", *fouts, ":rule", *fins)
|
||||||
|
emit(" command=", "&&".join([s.strip() for s in rule]))
|
||||||
|
if label:
|
||||||
|
emit(" description=", label)
|
||||||
|
emit("build", name, ":phony", *fouts)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
assert len(cmds) == 0, "rules with no outputs cannot have commands"
|
assert len(cmds) == 0, "rules with no outputs cannot have commands"
|
||||||
emit(name, ":", *fins, into=lines)
|
emit("build", name, ":phony", *fins)
|
||||||
|
|
||||||
outputFp.write("".join(lines))
|
|
||||||
emit("")
|
emit("")
|
||||||
|
|
||||||
|
|
||||||
@@ -585,47 +626,65 @@ def export(self, name=None, items: TargetsMap = {}, deps: Targets = []):
|
|||||||
dest = self.targetof(dest)
|
dest = self.targetof(dest)
|
||||||
outs += [dest]
|
outs += [dest]
|
||||||
|
|
||||||
destf = filenameof(dest)
|
destf = self.templateexpand(filenameof(dest))
|
||||||
|
outputTargets.update([destf])
|
||||||
|
|
||||||
srcs = filenamesof([src])
|
srcs = filenamesof([src])
|
||||||
assert (
|
assert (
|
||||||
len(srcs) == 1
|
len(srcs) == 1
|
||||||
), "a dependency of an exported file must have exactly one output file"
|
), "a dependency of an exported file must have exactly one output file"
|
||||||
|
srcf = self.templateexpand(srcs[0])
|
||||||
|
|
||||||
subrule = simplerule(
|
subrule = simplerule(
|
||||||
name=f"{self.localname}/{destf}",
|
name=f"{self.localname}/{destf}",
|
||||||
cwd=self.cwd,
|
cwd=self.cwd,
|
||||||
ins=[srcs[0]],
|
ins=[srcs[0]],
|
||||||
outs=[destf],
|
outs=[destf],
|
||||||
commands=["$(CP) -H %s %s" % (srcs[0], destf)],
|
commands=["$(CP) -H %s %s" % (srcf, destf)],
|
||||||
label="",
|
label="EXPORT",
|
||||||
)
|
)
|
||||||
subrule.materialise()
|
subrule.materialise()
|
||||||
|
|
||||||
self.ins = []
|
self.ins = []
|
||||||
self.outs = deps + outs
|
self.outs = deps + outs
|
||||||
|
outputTargets.add(name)
|
||||||
|
|
||||||
emit("")
|
emit("")
|
||||||
emit(".PHONY:", name)
|
emit(
|
||||||
emit(name, ":", *filenamesof(outs + deps))
|
"build",
|
||||||
|
name,
|
||||||
|
":phony",
|
||||||
|
*[self.templateexpand(f) for f in filenamesof(outs + deps)],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("-v", "--verbose", action="store_true")
|
|
||||||
parser.add_argument("-q", "--quiet", action="store_true")
|
parser.add_argument("-q", "--quiet", action="store_true")
|
||||||
parser.add_argument("-o", "--output")
|
parser.add_argument("-v", "--varfile")
|
||||||
|
parser.add_argument("-o", "--outputdir")
|
||||||
|
parser.add_argument("-D", "--define", action="append", default=[])
|
||||||
parser.add_argument("files", nargs="+")
|
parser.add_argument("files", nargs="+")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
global verbose
|
|
||||||
verbose = args.verbose
|
|
||||||
|
|
||||||
global quiet
|
global quiet
|
||||||
quiet = args.quiet
|
quiet = args.quiet
|
||||||
|
|
||||||
global outputFp
|
vardefs = args.define
|
||||||
outputFp = open(args.output, "wt")
|
if args.varfile:
|
||||||
|
with open(args.varfile, "rt") as fp:
|
||||||
|
vardefs = vardefs + list(fp)
|
||||||
|
|
||||||
|
for line in vardefs:
|
||||||
|
if "=" in line:
|
||||||
|
name, value = line.split("=", 1)
|
||||||
|
G.setdefault(name.strip(), value.strip())
|
||||||
|
|
||||||
|
global ninjaFp, shellFp, outputdir
|
||||||
|
outputdir = args.outputdir
|
||||||
|
G.setdefault("OBJ", outputdir)
|
||||||
|
ninjaFp = open(outputdir + "/build.ninja", "wt")
|
||||||
|
ninjaFp.write(f"include build/ab.ninja\n")
|
||||||
|
|
||||||
for k in ["Rule"]:
|
for k in ["Rule"]:
|
||||||
defaultGlobals[k] = globals()[k]
|
defaultGlobals[k] = globals()[k]
|
||||||
@@ -640,7 +699,10 @@ def main():
|
|||||||
while unmaterialisedTargets:
|
while unmaterialisedTargets:
|
||||||
t = next(iter(unmaterialisedTargets))
|
t = next(iter(unmaterialisedTargets))
|
||||||
t.materialise()
|
t.materialise()
|
||||||
emit("AB_LOADED = 1\n")
|
|
||||||
|
with open(outputdir + "/build.targets", "wt") as fp:
|
||||||
|
fp.write("ninja-targets =")
|
||||||
|
fp.write(substituteGlobalVariables(" ".join(outputTargets)))
|
||||||
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|||||||
55
build/c.py
55
build/c.py
@@ -7,23 +7,22 @@ from build.ab import (
|
|||||||
flatten,
|
flatten,
|
||||||
simplerule,
|
simplerule,
|
||||||
emit,
|
emit,
|
||||||
|
G,
|
||||||
)
|
)
|
||||||
from build.utils import filenamesmatchingof, stripext, collectattrs
|
from build.utils import stripext, collectattrs
|
||||||
from build.toolchain import Toolchain, HostToolchain
|
from build.toolchain import Toolchain, HostToolchain
|
||||||
from os.path import *
|
from os.path import *
|
||||||
|
|
||||||
emit(
|
if G.OSX != "yes":
|
||||||
"""
|
G.STARTGROUP = "-Wl,--start-group"
|
||||||
ifeq ($(OSX),no)
|
G.ENDGROUP = "-Wl,--end-group"
|
||||||
STARTGROUP ?= -Wl,--start-group
|
else:
|
||||||
ENDGROUP ?= -Wl,--end-group
|
G.STARTGROUP = ""
|
||||||
endif
|
G.ENDGROUP = ""
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
Toolchain.CC = ["$(CC) -c -o $[outs[0]] $[ins[0]] $(CFLAGS) $[cflags]"]
|
Toolchain.CC = ["$(CC) -c -o $[outs[0]] $[ins[0]] $(CFLAGS) $[cflags]"]
|
||||||
Toolchain.CPP = ["$(CC) -E -P -o $[outs] $[cflags] -x c $[ins]"]
|
Toolchain.CPP = ["$(CC) -E -P -o $[outs] $[cflags] -x c $[ins]"]
|
||||||
Toolchain.CXX = ["$(CXX) -c -o $[outs[0]] $[ins[0]] $(CFLAGS) $[cflags]"]
|
Toolchain.CXX = ["$(CXX) -c -o $[outs[0]] $[ins[0]] $(CXXFLAGS) $[cflags]"]
|
||||||
Toolchain.AR = ["$(AR) cqs $[outs[0]] $[ins]"]
|
Toolchain.AR = ["$(AR) cqs $[outs[0]] $[ins]"]
|
||||||
Toolchain.ARXX = ["$(AR) cqs $[outs[0]] $[ins]"]
|
Toolchain.ARXX = ["$(AR) cqs $[outs[0]] $[ins]"]
|
||||||
Toolchain.CLINK = [
|
Toolchain.CLINK = [
|
||||||
@@ -70,13 +69,9 @@ def _toolchain_find_header_targets(deps, initial=[]):
|
|||||||
Toolchain.find_c_header_targets = _toolchain_find_header_targets
|
Toolchain.find_c_header_targets = _toolchain_find_header_targets
|
||||||
|
|
||||||
|
|
||||||
HostToolchain.CC = [
|
HostToolchain.CC = ["$(HOSTCC) -c -o $[outs[0]] $[ins[0]] $(HOSTCFLAGS) $[cflags]"]
|
||||||
"$(HOSTCC) -c -o $[outs[0]] $[ins[0]] $(HOSTCFLAGS) $[cflags]"
|
|
||||||
]
|
|
||||||
HostToolchain.CPP = ["$(HOSTCC) -E -P -o $[outs] $[cflags] -x c $[ins]"]
|
HostToolchain.CPP = ["$(HOSTCC) -E -P -o $[outs] $[cflags] -x c $[ins]"]
|
||||||
HostToolchain.CXX = [
|
HostToolchain.CXX = ["$(HOSTCXX) -c -o $[outs[0]] $[ins[0]] $(HOSTCFLAGS) $[cflags]"]
|
||||||
"$(HOSTCXX) -c -o $[outs[0]] $[ins[0]] $(HOSTCFLAGS) $[cflags]"
|
|
||||||
]
|
|
||||||
HostToolchain.AR = ["$(HOSTAR) cqs $[outs[0]] $[ins]"]
|
HostToolchain.AR = ["$(HOSTAR) cqs $[outs[0]] $[ins]"]
|
||||||
HostToolchain.ARXX = ["$(HOSTAR) cqs $[outs[0]] $[ins]"]
|
HostToolchain.ARXX = ["$(HOSTAR) cqs $[outs[0]] $[ins]"]
|
||||||
HostToolchain.CLINK = [
|
HostToolchain.CLINK = [
|
||||||
@@ -102,9 +97,7 @@ def _indirect(deps, name):
|
|||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def cfileimpl(
|
def cfileimpl(self, name, srcs, deps, suffix, commands, label, toolchain, cflags):
|
||||||
self, name, srcs, deps, suffix, commands, label, toolchain, cflags
|
|
||||||
):
|
|
||||||
outleaf = "=" + stripext(basename(filenameof(srcs[0]))) + suffix
|
outleaf = "=" + stripext(basename(filenameof(srcs[0]))) + suffix
|
||||||
|
|
||||||
hdr_deps = toolchain.find_c_header_targets(deps)
|
hdr_deps = toolchain.find_c_header_targets(deps)
|
||||||
@@ -114,9 +107,7 @@ def cfileimpl(
|
|||||||
if ("cheader_deps" not in d.args) and ("clibrary_deps" not in d.args)
|
if ("cheader_deps" not in d.args) and ("clibrary_deps" not in d.args)
|
||||||
]
|
]
|
||||||
hdr_files = collectattrs(targets=hdr_deps, name="cheader_files")
|
hdr_files = collectattrs(targets=hdr_deps, name="cheader_files")
|
||||||
cflags = collectattrs(
|
cflags = collectattrs(targets=hdr_deps, name="caller_cflags", initial=cflags)
|
||||||
targets=hdr_deps, name="caller_cflags", initial=cflags
|
|
||||||
)
|
|
||||||
|
|
||||||
t = simplerule(
|
t = simplerule(
|
||||||
replaces=self,
|
replaces=self,
|
||||||
@@ -194,7 +185,7 @@ def findsources(self, srcs, deps, cflags, filerule, toolchain, cwd):
|
|||||||
for s in flatten(srcs):
|
for s in flatten(srcs):
|
||||||
objs += [
|
objs += [
|
||||||
filerule(
|
filerule(
|
||||||
name=join(self.localname, _removeprefix(f, "$(OBJ)/")),
|
name=join(self.localname, _removeprefix(f, G.OBJ + "/")),
|
||||||
srcs=[f],
|
srcs=[f],
|
||||||
deps=deps,
|
deps=deps,
|
||||||
cflags=sorted(set(cflags)),
|
cflags=sorted(set(cflags)),
|
||||||
@@ -239,9 +230,7 @@ def libraryimpl(
|
|||||||
i = 0
|
i = 0
|
||||||
for dest, src in hdrs.items():
|
for dest, src in hdrs.items():
|
||||||
s = filenamesof([src])
|
s = filenamesof([src])
|
||||||
assert (
|
assert len(s) == 1, "the target of a header must return exactly one file"
|
||||||
len(s) == 1
|
|
||||||
), "the target of a header must return exactly one file"
|
|
||||||
|
|
||||||
cs += [f"$(CP) $[ins[{i}]] $[outs[{i}]]"]
|
cs += [f"$(CP) $[ins[{i}]] $[outs[{i}]]"]
|
||||||
outs += ["=" + dest]
|
outs += ["=" + dest]
|
||||||
@@ -431,20 +420,16 @@ def programimpl(
|
|||||||
label,
|
label,
|
||||||
filerule,
|
filerule,
|
||||||
):
|
):
|
||||||
cfiles = findsources(
|
cfiles = findsources(self, srcs, deps, cflags, filerule, toolchain, self.cwd)
|
||||||
self, srcs, deps, cflags, filerule, toolchain, self.cwd
|
|
||||||
)
|
|
||||||
|
|
||||||
lib_deps = toolchain.find_c_library_targets(deps)
|
lib_deps = toolchain.find_c_library_targets(deps)
|
||||||
libs = collectattrs(targets=lib_deps, name="clibrary_files")
|
libs = collectattrs(targets=lib_deps, name="clibrary_files")
|
||||||
ldflags = collectattrs(
|
ldflags = collectattrs(targets=lib_deps, name="caller_ldflags", initial=ldflags)
|
||||||
targets=lib_deps, name="caller_ldflags", initial=ldflags
|
|
||||||
)
|
|
||||||
|
|
||||||
simplerule(
|
simplerule(
|
||||||
replaces=self,
|
replaces=self,
|
||||||
ins=cfiles + libs,
|
ins=cfiles + libs,
|
||||||
outs=[f"={self.localname}{toolchain.EXE}"],
|
outs=[f"={self.localname}$(EXT)"],
|
||||||
deps=deps,
|
deps=deps,
|
||||||
label=label,
|
label=label,
|
||||||
commands=commands,
|
commands=commands,
|
||||||
@@ -558,9 +543,7 @@ def hostcxxprogram(
|
|||||||
|
|
||||||
def _cppfileimpl(self, name, srcs, deps, cflags, toolchain):
|
def _cppfileimpl(self, name, srcs, deps, cflags, toolchain):
|
||||||
hdr_deps = _indirect(deps, "cheader_deps")
|
hdr_deps = _indirect(deps, "cheader_deps")
|
||||||
cflags = collectattrs(
|
cflags = collectattrs(targets=hdr_deps, name="caller_cflags", initial=cflags)
|
||||||
targets=hdr_deps, name="caller_cflags", initial=cflags
|
|
||||||
)
|
|
||||||
|
|
||||||
simplerule(
|
simplerule(
|
||||||
replaces=self,
|
replaces=self,
|
||||||
|
|||||||
10
build/pkg.py
10
build/pkg.py
@@ -1,4 +1,4 @@
|
|||||||
from build.ab import Rule, Target
|
from build.ab import Rule, Target, G
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
@@ -31,8 +31,8 @@ class _PkgConfig:
|
|||||||
return self.package_properties[p]
|
return self.package_properties[p]
|
||||||
|
|
||||||
|
|
||||||
TargetPkgConfig = _PkgConfig(os.getenv("PKG_CONFIG"))
|
TargetPkgConfig = _PkgConfig(G.PKG_CONFIG)
|
||||||
HostPkgConfig = _PkgConfig(os.getenv("HOST_PKG_CONFIG"))
|
HostPkgConfig = _PkgConfig(G.HOST_PKG_CONFIG)
|
||||||
|
|
||||||
|
|
||||||
def _package(self, name, package, fallback, pkgconfig):
|
def _package(self, name, package, fallback, pkgconfig):
|
||||||
@@ -49,9 +49,7 @@ def _package(self, name, package, fallback, pkgconfig):
|
|||||||
self.traits.update({"clibrary", "cxxlibrary"})
|
self.traits.update({"clibrary", "cxxlibrary"})
|
||||||
return
|
return
|
||||||
|
|
||||||
assert (
|
assert fallback, f"Required package '{package}' not installed"
|
||||||
fallback
|
|
||||||
), f"Required package '{package}' not installed when materialising target '$[name]'"
|
|
||||||
|
|
||||||
if "cheader_deps" in fallback.args:
|
if "cheader_deps" in fallback.args:
|
||||||
self.args["cheader_deps"] = fallback.args["cheader_deps"]
|
self.args["cheader_deps"] = fallback.args["cheader_deps"]
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
from build.ab import Rule, Targets, emit, simplerule, filenamesof
|
from build.ab import Rule, Targets, emit, simplerule, filenamesof, G
|
||||||
from build.utils import filenamesmatchingof, collectattrs
|
from build.utils import filenamesmatchingof, collectattrs
|
||||||
from os.path import join, abspath, dirname, relpath
|
from os.path import join, abspath, dirname, relpath
|
||||||
from build.pkg import has_package
|
from build.pkg import has_package
|
||||||
|
|
||||||
emit(
|
G.setdefault("PROTOC", "protoc")
|
||||||
"""
|
G.setdefault("HOSTPROTOC", "hostprotoc")
|
||||||
PROTOC ?= protoc
|
|
||||||
HOSTPROTOC ?= protoc
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
assert has_package("protobuf"), "required package 'protobuf' not installed"
|
assert has_package("protobuf"), "required package 'protobuf' not installed"
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import platform
|
|
||||||
|
|
||||||
_is_windows = (platform.system() == "Windows")
|
|
||||||
|
|
||||||
class Toolchain:
|
class Toolchain:
|
||||||
PREFIX = ""
|
PREFIX = ""
|
||||||
EXE = ".exe" if _is_windows else ""
|
|
||||||
|
|
||||||
|
|
||||||
class HostToolchain(Toolchain):
|
class HostToolchain(Toolchain):
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from build.ab import (
|
|||||||
from os.path import relpath, splitext, join, basename, isfile
|
from os.path import relpath, splitext, join, basename, isfile
|
||||||
from glob import iglob
|
from glob import iglob
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
def filenamesmatchingof(xs, pattern):
|
def filenamesmatchingof(xs, pattern):
|
||||||
@@ -51,6 +52,11 @@ def itemsof(pattern, root=None, cwd=None):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def shell(args):
|
||||||
|
r = subprocess.check_output(args)
|
||||||
|
return r.decode("utf-8").strip()
|
||||||
|
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
def objectify(self, name, src: Target, symbol):
|
def objectify(self, name, src: Target, symbol):
|
||||||
simplerule(
|
simplerule(
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ from build.ab import (
|
|||||||
|
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
def zip(
|
def zip(self, name, flags="", items: TargetsMap = {}, extension="zip", label="ZIP"):
|
||||||
self, name, flags="", items: TargetsMap = {}, extension="zip", label="ZIP"
|
|
||||||
):
|
|
||||||
cs = ["$(PYTHON) build/_zip.py -z $[outs]"]
|
cs = ["$(PYTHON) build/_zip.py -z $[outs]"]
|
||||||
|
|
||||||
ins = []
|
ins = []
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from build.c import clibrary
|
|||||||
from build.zip import zip
|
from build.zip import zip
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from os.path import *
|
from os.path import *
|
||||||
|
import config
|
||||||
|
|
||||||
icons = ["fluxfile", "hardware", "icon", "imagefile"]
|
icons = ["fluxfile", "hardware", "icon", "imagefile"]
|
||||||
|
|
||||||
@@ -17,37 +18,37 @@ clibrary(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
simplerule(
|
if config.osx:
|
||||||
name="fluxengine_icns",
|
simplerule(
|
||||||
ins=["./icon.png"],
|
name="fluxengine_icns",
|
||||||
outs=["=fluxengine.icns"],
|
ins=["./icon.png"],
|
||||||
commands=[
|
outs=["=fluxengine.icns"],
|
||||||
"mkdir -p fluxengine.iconset",
|
commands=[
|
||||||
"sips -z 64 64 $[ins[0]] --out fluxengine.iconset/icon_32x32@2x.png > /dev/null",
|
"mkdir -p fluxengine.iconset",
|
||||||
"iconutil -c icns -o $[outs[0]] fluxengine.iconset",
|
"sips -z 64 64 $[ins[0]] --out fluxengine.iconset/icon_32x32@2x.png > /dev/null",
|
||||||
],
|
"iconutil -c icns -o $[outs[0]] fluxengine.iconset",
|
||||||
label="ICONSET",
|
],
|
||||||
)
|
label="ICONSET",
|
||||||
|
)
|
||||||
simplerule(
|
|
||||||
name="fluxengine_ico",
|
template_files = [
|
||||||
ins=["./icon.png"],
|
f
|
||||||
outs=["=fluxengine.ico"],
|
for f in glob("**", recursive=True, root_dir="extras/FluxEngine.app.template")
|
||||||
commands=["png2ico $[outs[0]] $[ins[0]]"],
|
if isfile(join("extras/FluxEngine.app.template", f))
|
||||||
label="MAKEICON",
|
]
|
||||||
)
|
zip(
|
||||||
|
name="fluxengine_template",
|
||||||
template_files = [
|
items={
|
||||||
f
|
join("FluxEngine.app", k): join("extras/FluxEngine.app.template", k)
|
||||||
for f in glob(
|
for k in template_files
|
||||||
"**", recursive=True, root_dir="extras/FluxEngine.app.template"
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if config.windows:
|
||||||
|
simplerule(
|
||||||
|
name="fluxengine_ico",
|
||||||
|
ins=["./icon.png"],
|
||||||
|
outs=["=fluxengine.ico"],
|
||||||
|
commands=["png2ico $[outs[0]] $[ins[0]]"],
|
||||||
|
label="MAKEICON",
|
||||||
)
|
)
|
||||||
if isfile(join("extras/FluxEngine.app.template", f))
|
|
||||||
]
|
|
||||||
zip(
|
|
||||||
name="fluxengine_template",
|
|
||||||
items={
|
|
||||||
join("FluxEngine.app", k): join("extras/FluxEngine.app.template", k)
|
|
||||||
for k in template_files
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,19 +1,25 @@
|
|||||||
from build.ab import emit, simplerule
|
from build.ab import simplerule, G
|
||||||
from build.c import cxxprogram
|
from build.c import cxxprogram
|
||||||
|
from build.utils import shell
|
||||||
from glob import glob
|
from glob import glob
|
||||||
import config
|
import config
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
emit(
|
G.setdefault("WX_CONFIG", "wx-config")
|
||||||
"""
|
assert shutil.which(G.WX_CONFIG), "Required binary 'wx-config' not found"
|
||||||
WX_CONFIG ?= wx-config
|
|
||||||
ifneq ($(strip $(shell command -v $(WX_CONFIG) >/dev/null 2>&1; echo $$?)),0)
|
G.setdefault(
|
||||||
WX_CFLAGS = $(error Required binary 'wx-config' not found.)
|
"WX_CFLAGS",
|
||||||
WX_LDFLAGS = $(error Required binary 'wx-config' not found.)
|
shell(
|
||||||
else
|
[G.WX_CONFIG, "--cxxflags", "base", "adv", "aui", "richtext", "core"]
|
||||||
WX_CFLAGS := $(shell $(WX_CONFIG) --cxxflags base adv aui richtext core)
|
),
|
||||||
WX_LDFLAGS := $(shell $(WX_CONFIG) --libs base adv aui richtext core)
|
)
|
||||||
endif
|
G.setdefault(
|
||||||
"""
|
"WX_LDFLAGS",
|
||||||
|
shell(
|
||||||
|
[G.WX_CONFIG, "--libs", "base", "adv", "aui", "richtext", "core"]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
extrasrcs = ["./layout.cpp"]
|
extrasrcs = ["./layout.cpp"]
|
||||||
|
|||||||
Reference in New Issue
Block a user