Fumofumotris/build.py

114 lines
3 KiB
Python
Raw Normal View History

2024-05-10 07:38:08 +00:00
import json
import subprocess
2024-05-10 21:59:52 +00:00
from subprocess import PIPE
from hashlib import md5
2024-05-10 07:38:08 +00:00
from pathlib import Path
2024-03-25 05:34:59 +00:00
2024-05-10 07:38:08 +00:00
GCC = "gcc"
2024-05-10 21:59:52 +00:00
ARGS = "-fdiagnostics-color -pthread -Wall -std=c17 -pedantic -g"
2024-03-25 05:34:59 +00:00
2024-05-10 07:38:08 +00:00
SOURCE_DIR = Path("source/")
OBJECT_DIR = Path("objects/")
OUTPUT = Path("debug")
2024-03-25 05:34:59 +00:00
2024-05-10 07:38:08 +00:00
CHECKSUMS = Path("checksums.txt")
ERRORS = Path("errors.txt")
2024-03-25 05:34:59 +00:00
2024-05-10 21:59:52 +00:00
def scan_checksums(files: list[Path]) -> list[str]:
return (md5(file.read_bytes()).hexdigest() for file in files)
2024-03-25 05:34:59 +00:00
2024-05-10 21:59:52 +00:00
def read_txt(txt: Path) -> any:
2024-05-10 07:38:08 +00:00
if not txt.exists():
2024-05-10 21:59:52 +00:00
return []
content = txt.read_text("utf-8")
return json.loads(content) if content else []
2024-03-25 05:34:59 +00:00
2024-05-10 21:59:52 +00:00
def write_txt(txt: Path, content) -> None:
txt.write_text(json.dumps(content))
2024-03-25 05:34:59 +00:00
2024-05-10 21:59:52 +00:00
def difference(cur, old, vals):
return (vals[cur.index(i)] for i in set(cur) - set(old))
2024-03-25 05:34:59 +00:00
2024-05-10 07:38:08 +00:00
def clean_objects(object_dir: Path, sources: list[Path]) -> None:
objects: list[Path] = [object for object in object_dir.rglob("*.o")]
2024-03-25 05:34:59 +00:00
2024-05-10 07:38:08 +00:00
object_stems = [object.stem for object in objects]
source_stems = [source.stem for source in sources]
2024-03-25 05:34:59 +00:00
2024-05-10 07:38:08 +00:00
for stem in set(object_stems).difference(source_stems):
objects[object_stems.index(stem)].unlink()
2024-03-25 05:34:59 +00:00
2024-05-10 21:59:52 +00:00
def header_dependencies(sources):
tasks = []
for source in sources:
task = subprocess.Popen(f"{GCC} -MMD {source} {ARGS} -o stdout", stdout=PIPE)
tasks.append(task)
for task in tasks:
2024-05-10 07:38:08 +00:00
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
2024-03-25 05:34:59 +00:00
2024-05-10 07:38:08 +00:00
args = f"{GCC} -c {source} {ARGS} {include_arg} -o {output}"
2024-03-25 05:34:59 +00:00
2024-05-10 07:38:08 +00:00
return subprocess.Popen(args, stderr=subprocess.PIPE)
2024-03-25 05:34:59 +00:00
2024-05-10 07:38:08 +00:00
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:
2024-05-10 21:59:52 +00:00
subprocess.run(f"{GCC} {object_dir}/*.o {ARGS} -o {output}")
def build(src: Path, object_dir: Path, output: Path):
includes, headers, sources = zip(map(src.rglob, ["*/", "*.h", "*.c"]))
2024-05-10 07:38:08 +00:00
2024-05-10 21:59:52 +00:00
headers_cur, sources_cur = map(scan_checksums, (headers, sources))
headers_old, sources_old = read_txt(CHECKSUMS)
2024-05-10 07:38:08 +00:00
2024-05-10 21:59:52 +00:00
headers_updt = difference(headers_cur, headers_old, headers)
sources_updt = difference(sources_cur, sources_old, sources)
2024-05-10 07:38:08 +00:00
2024-05-10 21:59:52 +00:00
dependencies = {}
2024-05-10 07:38:08 +00:00
tasks = []
for source in updated_sources:
tasks.append(compile(source, includes, object_dir))
errors = wait_compile_tasks(tasks, updated_sources).values()
print("\n".join(err for err in errors if err is not False).strip("\n"))
clean_objects(object_dir, all_sources)
link(object_dir, output)
print(f"Compiled: {len(updated_sources)} Linked: {len(all_sources)}")
if __name__ == "__main__":
2024-05-10 21:59:52 +00:00
build(SOURCE_DIR, OBJECT_DIR, OUTPUT)