df
df
This commit is contained in:
parent
5c55be1ff9
commit
8b482a0bb6
147
build.py
147
build.py
|
@ -11,18 +11,19 @@ ARGS = "-fdiagnostics-color -pthread -Wall -std=c17 -pedantic -g"
|
|||
|
||||
SOURCE_DIR = Path("source/")
|
||||
OBJECT_DIR = Path("objects/")
|
||||
OUTPUT = Path("debug")
|
||||
OUTPUT = Path("build/debug")
|
||||
|
||||
|
||||
CHECKSUMS = Path("checksums.txt")
|
||||
ERRORS = Path("errors.txt")
|
||||
SRC_CHECKSUMS_TXT = Path("build/src_checksums.txt")
|
||||
HDR_CHECKSUMS_TXT = Path("build/hdr_checksums.txt")
|
||||
ERRORS_TXT = Path("build/errors.txt")
|
||||
|
||||
|
||||
def scan_checksums(files: list[Path]) -> list[str]:
|
||||
return (md5(file.read_bytes()).hexdigest() for file in files)
|
||||
def scan_checksums(files) -> list[str]:
|
||||
return [md5(file.read_bytes()).hexdigest() for file in files]
|
||||
|
||||
|
||||
def read_txt(txt: Path) -> any:
|
||||
def read_text(txt: Path) -> any:
|
||||
if not txt.exists():
|
||||
return []
|
||||
|
||||
|
@ -30,84 +31,108 @@ def read_txt(txt: Path) -> any:
|
|||
return json.loads(content) if content else []
|
||||
|
||||
|
||||
def write_txt(txt: Path, content) -> None:
|
||||
def write_text(txt: Path, content) -> None:
|
||||
txt.write_text(json.dumps(content))
|
||||
|
||||
|
||||
def difference(cur, old, vals):
|
||||
return (vals[cur.index(i)] for i in set(cur) - set(old))
|
||||
def checksum_difs(cur, old, files) -> set[str]:
|
||||
dif = set(cur) - set(old)
|
||||
return ({files[cur.index(i)] for i in dif}, dif)
|
||||
|
||||
|
||||
def clean_objects(object_dir: Path, sources: list[Path]) -> None:
|
||||
objects: list[Path] = [object for object in object_dir.rglob("*.o")]
|
||||
def delete_unused_objects(object_dir: Path, source_stems: list[str]) -> None:
|
||||
objects = [object for object in object_dir.rglob("*.o")]
|
||||
|
||||
object_stems = [object.stem for object in objects]
|
||||
source_stems = [source.stem for source in sources]
|
||||
|
||||
for stem in set(object_stems).difference(source_stems):
|
||||
objects[object_stems.index(stem)].unlink()
|
||||
map(Path.unlink, [object for object in objects if object.stem not in source_stems])
|
||||
|
||||
|
||||
def header_dependencies(sources):
|
||||
tasks = []
|
||||
def paths_to_args(args: list[Path], sep: str="") -> str:
|
||||
return " ".join(f"{sep}{arg}" for arg in args)
|
||||
|
||||
|
||||
def dispatch(args):
|
||||
return subprocess.Popen(args, stdout=PIPE, stderr=PIPE, text=True, encoding="utf-8")
|
||||
|
||||
|
||||
def header_deps(sources, includes):
|
||||
incl = paths_to_args(includes, "-I ")
|
||||
src = paths_to_args(sources)
|
||||
out, err = dispatch(f"{GCC} -MM {src} {ARGS} {incl}").communicate()
|
||||
|
||||
dependencies = []
|
||||
if out:
|
||||
for line in out.splitlines():
|
||||
if ":" in line:
|
||||
dependencies.append(set())
|
||||
|
||||
dependencies[-1].add(Path(line.strip(" \\")))
|
||||
|
||||
return dependencies
|
||||
|
||||
|
||||
def compile_job(sources, includes, errors, object_dir):
|
||||
incl = paths_to_args(includes, "-I ")
|
||||
|
||||
processes = []
|
||||
for source in sources:
|
||||
task = subprocess.Popen(f"{GCC} -MMD {source} {ARGS} -o stdout", stdout=PIPE)
|
||||
tasks.append(task)
|
||||
output = object_dir.joinpath(source.with_suffix(".o").name)
|
||||
|
||||
for task in tasks:
|
||||
|
||||
processes.append(dispatch(f"{GCC} -c {source} {ARGS} {incl} -o {output}"))
|
||||
|
||||
for process, source in zip(processes, sources):
|
||||
out, err = process.communicate()
|
||||
errors[source.stem] = err
|
||||
|
||||
|
||||
def compile(source: Path, includes: list[Path], object_dir: Path):
|
||||
include_arg: str = " ".join(f"-I {dir}" for dir in includes)
|
||||
output: Path = object_dir / source.with_suffix(".o").name
|
||||
|
||||
args = f"{GCC} -c {source} {ARGS} {include_arg} -o {output}"
|
||||
|
||||
return subprocess.Popen(args, stderr=subprocess.PIPE)
|
||||
|
||||
|
||||
def wait_compile_tasks(tasks, updated_sources) -> dict[str, str]:
|
||||
errors = disk_read_errors(ERRORS)
|
||||
|
||||
for task, source in zip(tasks, updated_sources):
|
||||
out, err = task.communicate()
|
||||
if err:
|
||||
errors[str(source)] = err.decode("utf-8")
|
||||
else:
|
||||
errors[str(source)] = False
|
||||
|
||||
disk_write_errors(ERRORS, errors)
|
||||
return errors
|
||||
|
||||
|
||||
def link(object_dir: Path, output: Path) -> None:
|
||||
subprocess.run(f"{GCC} {object_dir}/*.o {ARGS} -o {output}")
|
||||
def link_job(object_dir, output):
|
||||
out, err = dispatch(f"{GCC} {object_dir}/*.o {ARGS} -o {output}").communicate()
|
||||
return err
|
||||
|
||||
|
||||
def build(src: Path, object_dir: Path, output: Path):
|
||||
includes, headers, sources = zip(map(src.rglob, ["*/", "*.h", "*.c"]))
|
||||
# Walk source directory, obtain checksums
|
||||
includes, headers, sources = map(list, map(src.rglob, ["*/", "*.h", "*.c"]))
|
||||
|
||||
headers_cur, sources_cur = map(scan_checksums, (headers, sources))
|
||||
headers_old, sources_old = read_txt(CHECKSUMS)
|
||||
hc_cur, sc_cur = scan_checksums(headers), scan_checksums(sources)
|
||||
hc_old, sc_old = read_text(HDR_CHECKSUMS_TXT), read_text(SRC_CHECKSUMS_TXT)
|
||||
|
||||
headers_updt = difference(headers_cur, headers_old, headers)
|
||||
sources_updt = difference(sources_cur, sources_old, sources)
|
||||
# Find out which sources need to be compiled based on checksum differences
|
||||
# and dependencies on changed headers
|
||||
header_updates, hc_updates = checksum_difs(hc_cur, hc_old, headers)
|
||||
source_updates, sc_updates = checksum_difs(sc_cur, sc_old, sources)
|
||||
|
||||
dependencies = {}
|
||||
for source_dependencies, source in zip(header_deps(sources, includes), sources):
|
||||
if any(header in header_updates for header in source_dependencies):
|
||||
source_updates.add(source)
|
||||
|
||||
tasks = []
|
||||
for source in updated_sources:
|
||||
tasks.append(compile(source, includes, object_dir))
|
||||
# Compile step: Read old error messages, then update, print, and write them
|
||||
errors = dict(read_text(ERRORS_TXT))
|
||||
compile_job(source_updates, includes, errors, object_dir)
|
||||
|
||||
errors = wait_compile_tasks(tasks, updated_sources).values()
|
||||
error_amt = 0
|
||||
source_stems = [source.stem for source in sources]
|
||||
for source_stem, message in list(errors.items()):
|
||||
if source_stem not in source_stems:
|
||||
errors.pop(source_stem)
|
||||
elif message:
|
||||
print(message)
|
||||
error_amt += 1
|
||||
else:
|
||||
sc_updates.remove(sc_cur[source_stems.index(source_stem)])
|
||||
|
||||
print("\n".join(err for err in errors if err is not False).strip("\n"))
|
||||
write_text(HDR_CHECKSUMS_TXT, list(hc_updates))
|
||||
write_text(SRC_CHECKSUMS_TXT, list(sc_updates))
|
||||
write_text(ERRORS_TXT, errors)
|
||||
|
||||
# Link step: Delete unused objects, link and print the error message
|
||||
delete_unused_objects(object_dir, [source.stem for source in sources])
|
||||
link_err = link_job(object_dir, output)
|
||||
|
||||
clean_objects(object_dir, all_sources)
|
||||
link(object_dir, output)
|
||||
print(f"Compiled: {len(updated_sources)} Linked: {len(all_sources)}")
|
||||
if link_err:
|
||||
print(link_err)
|
||||
|
||||
# yippee
|
||||
print(f"Compiled: {len(source_updates)} Linked: {len(sources)} Errored: {error_amt}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
BIN
build/debug.exe
Normal file
BIN
build/debug.exe
Normal file
Binary file not shown.
1
build/errors.txt
Normal file
1
build/errors.txt
Normal file
|
@ -0,0 +1 @@
|
|||
{"tetr": "", "ringbuffer": "", "event": "", "ctrl": "", "terminal": "", "fumoengine": "", "parseinput": "", "dictionary": "", "fumocommon": "", "win": "", "input": "", "fumotris": "", "vector": ""}
|
1
build/hdr_checksums.txt
Normal file
1
build/hdr_checksums.txt
Normal file
|
@ -0,0 +1 @@
|
|||
["0d7597ffd48812c52d3f02731cbf693f", "6669fc7fcffc77563f1315cb7710ec82", "fbccab3d5cd1838cbf8ad1d4e2a7c03b", "1d6bdf7e8de4ac751252b67027e5d57e", "330b6f9493d091e7c340c1434b6f80b4", "a3dca3fbce775b791384bec8abc3f0c3", "f61af2abe56cd6881a830ae6222483e9", "1cdcae043579aa6406b9823b5f52aedd", "088145536b04ef82517eb93630888dff", "1420f33d62a8ed8429c8f1a96f1ab33e", "e38bd5ea2b554a21849158242a2add8e", "9df90eabc8908cac60aa774d335a106c", "333c7211bb2c72ad321495f53d11bff0", "dc6df72158812bc9f6ed484a4cfb046b"]
|
1
build/src_checksums.txt
Normal file
1
build/src_checksums.txt
Normal file
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -1 +0,0 @@
|
|||
{"source\\fumoengine\\fumocommon.c": "bdd12a9257829d191f57d442b068db37", "source\\fumoengine\\fumoengine.c": "f3097febe6401acf6d0d8b3032b2da68", "source\\fumotris\\fumotris.c": "1516eb830de511f7987507be279e6f02", "source\\fumotris\\tetr.c": "74901782ad0d235bb6b078d2bb6ef99e", "source\\fumoengine\\include\\dictionary.c": "c8f8e5f99965c5c82caf24790fed78e4", "source\\fumoengine\\include\\event.c": "140ba30120636d5d33875e43447e0642", "source\\fumoengine\\include\\ringbuffer.c": "0d552f9daeeebd7ef23f4aaa7ae3e022", "source\\fumoengine\\include\\vector.c": "3f2ccd6831013ac0428446c776891503", "source\\fumoengine\\input\\ctrl.c": "6113d9b6cbcef5daed308f96a0a96f18", "source\\fumoengine\\input\\input.c": "7004fde9604dd57325d623cf9775b33c", "source\\fumoengine\\terminal\\terminal.c": "42b3d4217c15403b6dd079cee4a68186", "source\\fumoengine\\input\\platforms\\parseinput.c": "e23dafd399743096db434a9869b3b47e", "source\\fumoengine\\input\\platforms\\win.c": "f52c9c1bab5d1f05d78f0420780d486a"}
|
|
@ -1 +0,0 @@
|
|||
{"source\\fumoengine\\terminal\\terminal.c": false, "source\\fumoengine\\fumocommon.c": false, "source\\fumoengine\\include\\event.c": false, "source\\fumoengine\\include\\ringbuffer.c": false, "source\\fumoengine\\input\\platforms\\win.c": false, "source\\fumotris\\tetr.c": "\u001b[01m\u001b[Ksource\\fumotris\\tetr.c:\u001b[m\u001b[K In function '\u001b[01m\u001b[KTetrMapDraw\u001b[m\u001b[K':\n\u001b[01m\u001b[Ksource\\fumotris\\tetr.c:43:31:\u001b[m\u001b[K \u001b[01;31m\u001b[Kerror: \u001b[m\u001b[Kunknown type name '\u001b[01m\u001b[Kff\u001b[m\u001b[K'\n 43 | block[0].ch = '(';\u001b[01;31m\u001b[Kff\u001b[m\u001b[K\n | \u001b[01;31m\u001b[K^~\u001b[m\u001b[K\n\u001b[01m\u001b[Ksource\\fumotris\\tetr.c:44:21:\u001b[m\u001b[K \u001b[01;31m\u001b[Kerror: \u001b[m\u001b[Kexpected '\u001b[01m\u001b[K=\u001b[m\u001b[K', '\u001b[01m\u001b[K,\u001b[m\u001b[K', '\u001b[01m\u001b[K;\u001b[m\u001b[K', '\u001b[01m\u001b[Kasm\u001b[m\u001b[K' or '\u001b[01m\u001b[K__attribute__\u001b[m\u001b[K' before '\u001b[01m\u001b[K.\u001b[m\u001b[K' token\n 44 | block[1]\u001b[01;31m\u001b[K.\u001b[m\u001b[Kch = ')';\n | \u001b[01;31m\u001b[K^\u001b[m\u001b[K\n", "source\\fumoengine\\include\\vector.c": false, "source\\fumoengine\\input\\platforms\\parseinput.c": false, "source\\fumoengine\\include\\dictionary.c": false, "source\\fumoengine\\input\\ctrl.c": false, "source\\fumoengine\\input\\input.c": false, "source\\fumotris\\fumotris.c": false, "source\\fumoengine\\fumoengine.c": false}
|
BIN
objects/ctrl.o
BIN
objects/ctrl.o
Binary file not shown.
Binary file not shown.
BIN
objects/event.o
BIN
objects/event.o
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
objects/input.o
BIN
objects/input.o
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
objects/tetr.o
BIN
objects/tetr.o
Binary file not shown.
BIN
objects/vector.o
BIN
objects/vector.o
Binary file not shown.
BIN
objects/win.o
BIN
objects/win.o
Binary file not shown.
Loading…
Reference in a new issue