df
This commit is contained in:
Julia 2024-05-13 02:11:02 -05:00
parent 5c55be1ff9
commit 8b482a0bb6
22 changed files with 89 additions and 63 deletions

147
build.py
View file

@ -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

Binary file not shown.

1
build/errors.txt Normal file
View file

@ -0,0 +1 @@
{"tetr": "", "ringbuffer": "", "event": "", "ctrl": "", "terminal": "", "fumoengine": "", "parseinput": "", "dictionary": "", "fumocommon": "", "win": "", "input": "", "fumotris": "", "vector": ""}

1
build/hdr_checksums.txt Normal file
View 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
View file

@ -0,0 +1 @@
[]

View file

@ -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"}

BIN
debug.exe

Binary file not shown.

View file

@ -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}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
test.exe

Binary file not shown.