1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
"""Compile a test program."""
from __future__ import annotations
import dataclasses
import os
import pathlib
import shlex
import subprocess # noqa: S404
import sys
import tempfile
import typing
from pychunk import common
if typing.TYPE_CHECKING:
from typing import Final
@dataclasses.dataclass(frozen=True)
class Config(common.Config):
"""Runtime configuration."""
tempd: pathlib.Path
source: pathlib.Path
obj: pathlib.Path
program: pathlib.Path
uncompressed: pathlib.Path
def parse_args(dirname: str) -> Config:
"""Parse the command-line arguments, deduce some things."""
parser: Final = common.base_parser("compile")
parser.add_argument(
"source",
type=str,
help="path to the test program source file",
)
args: Final = parser.parse_args()
tempd: Final = pathlib.Path(dirname).absolute()
return Config(
tempd=tempd,
bindir=pathlib.Path(args.bindir),
source=pathlib.Path(args.source),
obj=tempd / "chunk.o",
program=tempd / "chunk",
env=common.get_runenv(),
orig=pathlib.Path(args.filename).absolute(),
compressed=tempd / "words.txt.zck",
uncompressed=tempd / "chunk.txt",
)
def call_pkg_config(cfg: Config, option: str, what: str) -> list[str]:
"""Invoke pkg-config, parse its output."""
print(f"Fetching the {what} for zck")
raw: Final = subprocess.check_output(
["pkg-config", option, "zck"],
encoding="UTF_8",
env=cfg.env,
).splitlines()
if len(raw) != 1:
sys.exit(f"`pkg-config {option} zck` returned {raw!r}")
return shlex.split(raw[0])
def do_compile(cfg: Config) -> None:
"""Compile the test program."""
cflags: Final = call_pkg_config(cfg, "--cflags", "C compiler flags")
if cfg.obj.exists():
sys.exit(f"Did not expect {cfg.obj} to exist")
cmd_compile: Final[list[pathlib.Path | str]] = ["cc", "-c", "-o", cfg.obj, *cflags, cfg.source]
print(f"Running {cmd_compile!r}")
subprocess.check_call(cmd_compile, env=cfg.env)
if not cfg.obj.is_file():
sys.exit(f"{cmd_compile!r} did not create the {cfg.obj} file")
libs: Final = call_pkg_config(cfg, "--libs", "C linker flags and libraries")
if cfg.program.exists():
sys.exit(f"Did not expect {cfg.program} to exist")
cmd_link: Final[list[pathlib.Path | str]] = ["cc", "-o", cfg.program, cfg.obj, *libs]
print(f"Running {cmd_link!r}")
subprocess.check_call(cmd_link, env=cfg.env)
if not cfg.program.is_file():
sys.exit(f"{cmd_link!r} did not create the {cfg.program} file")
if not os.access(cfg.program, os.X_OK):
sys.exit(f"Not an executable file: {cfg.program}")
print(f"Looks like we got {cfg.program}")
def run_program(cfg: Config) -> None:
"""Run the test program, hopefully generate the chunk file."""
print(f"About to run {cfg.program}")
if cfg.uncompressed.exists():
sys.exit(f"Did not expect {cfg.uncompressed} to exist")
subprocess.check_call(
[cfg.program, cfg.compressed, cfg.uncompressed],
shell=False,
env=cfg.env,
)
if not cfg.uncompressed.is_file():
sys.exit(f"{cfg.program} did not create the {cfg.uncompressed} file")
def compare_chunk(cfg: Config, second: common.Chunk, orig_size: int) -> None:
"""Read data from the input file and the chunk."""
# OK, let's load it all into memory, mmkay?
contents: Final = cfg.orig.read_bytes()
if len(contents) != orig_size:
sys.exit(f"Could not read {orig_size} bytes from {cfg.orig}, read {len(contents)}")
chunk: Final = cfg.uncompressed.read_bytes()
if len(chunk) != second.size:
sys.exit(f"Could not read {second.size} bytes from {cfg.uncompressed}, read {len(chunk)}")
if contents[second.start : second.start + second.size] != chunk:
sys.exit("Mismatch!")
def main() -> None:
"""Parse arguments, compile a program, compress a file, test it."""
with tempfile.TemporaryDirectory() as dirname:
print(f"Using temporary directory {dirname}")
cfg: Final = parse_args(dirname)
do_compile(cfg)
orig_size: Final = cfg.orig.stat().st_size
print(f"Original file size: {orig_size}")
comp_size: Final = common.do_compress(cfg, orig_size)
second_chunk: Final = common.read_chunks(cfg, orig_size, comp_size)
run_program(cfg)
compare_chunk(cfg, second_chunk, orig_size)
print("Seems fine!")
if __name__ == "__main__":
main()
|