File: compile.py

package info (click to toggle)
zchunk 1.5.2%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,704 kB
  • sloc: ansic: 13,244; python: 457; sh: 260; makefile: 13
file content (141 lines) | stat: -rw-r--r-- 4,571 bytes parent folder | download | duplicates (2)
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()