diff --git a/bytebeat_compiler.py b/bytebeat_compiler.py index d4823a7..b87ea38 100644 --- a/bytebeat_compiler.py +++ b/bytebeat_compiler.py @@ -4,12 +4,12 @@ if __name__ == "__main__": print(":: C bytebeat generator: compiler unit") from argparse import ArgumentParser -from os import environ, listdir, makedirs, name as os_name, \ - remove as delete_file, rmdir -from os.path import exists, join as path_join +from os import environ, makedirs, name as os_name, rename +from os.path import basename, exists, join as path_join from shlex import join as command_line_join, split as command_line_split from shutil import which from sys import stdin, stdout +from tempfile import TemporaryDirectory from typing import Dict, Union import subprocess @@ -32,8 +32,8 @@ PATHS = { # Resolve paths PATHS["template"] = path_join(PATHS["src_dir"], PATHS["template"]) -PATHS["substitute"] = path_join(PATHS["bin_dir"], PATHS["substitute"]) -PATHS["output"] = path_join(PATHS["bin_dir"], PATHS["output"]) +PATHS["substitute_kept"] = path_join(PATHS["bin_dir"], PATHS["substitute"]) +PATHS["output_kept"] = path_join(PATHS["bin_dir"], PATHS["output"]) PATHS["fwrite_le"] = path_join(PATHS["src_dir"], PATHS["fwrite_le"]) # Add `.` directory before all paths for compilation @@ -47,8 +47,8 @@ DEFAULT_PARAMETERS = { "CFLAGS": "-Ofast -march=native -mtune=native -Wall -Wextra -Wpedantic " "-pedantic -Wno-unused-variable -Wno-unused-but-set-variable " "-Wno-dangling-else -Wno-parentheses -std=c99", - "INPUT_FILE": PATHS["substitute"], - "OUTPUT_FILE": PATHS["output"] + "INPUT_FILE": PATHS["substitute_kept"], + "OUTPUT_FILE": PATHS["output_kept"] } stdout_atty = hasattr(stdout, "isatty") and stdout.isatty() @@ -94,10 +94,6 @@ def run_command(*command: list[str]) -> None: if subprocess.run(command).returncode != EXIT_SUCCESS: raise SystemExit(EXIT_FAILURE) -def delete_empty_dir(path: str) -> None: - if exists(path) and len(listdir(path)) == 0: - rmdir(path) - preprocessor_bool = lambda value: "1" if value else "0" C_str_repr = lambda s: '"' + s.replace("\\", "\\\\").replace(r'"', r'\"') + '"' @@ -187,8 +183,6 @@ if __name__ == "__main__": raise SystemExit("Empty file or STDIN") # - Compilation - makedirs(PATHS["bin_dir"], exist_ok=True) - if not args.no_return: # Insert `return` statement # XXX: The bytebeat code is enclosed in parentheses to allow for the # use of commas as a comma operator, enabling more formulas to function. @@ -243,30 +237,6 @@ if __name__ == "__main__": ansi_escape_codes_supported = args.color == "auto" and stdout_atty or \ args.color == "always" - rewrite_file(PATHS["substitute"], substitute_vars({ - "bytebeat_contents": bytebeat_contents, - "output_file": C_str_repr(args.output), - "sample_rate": \ - value if (value := args.final_sample_rate) else args.sample_rate, - "original_sample_rate": original_sample_rate, - "final_sample_rate_code": final_sample_rate_code, - "bit_depth": args.bit_depth, - "is_signed": args.signed, - "precalculated_ratio": args.precalculate_ratio, - "faster_sample_ratio_math": args.precalculate_ratio, - "fp_return_type": args.floating_point, - "channels": args.channels, - "length": samples, - "wav_product": gen_length * (args.bit_depth // BITS_PER_BYTE), - "gen_length": gen_length, - "sequential_mode": args.mode == "sequential", - "block_size": args.block_size, - "silent_mode": args.silent, - "verbose_mode": args.verbose and not args.silent, - "fwrite_le": PATHS["fwrite_le_header"], - "ansi_escape_codes_supported": ansi_escape_codes_supported - }, read_file(PATHS["template"]), args.show_substituted_values)) - if is_cmd_unavailable(CC): print(f"Compiler {CC} is not available, searching:") @@ -286,20 +256,52 @@ if __name__ == "__main__": "specify it by setting\nan environmental variable " "CC.") - # Compile - print("Compiling") + with TemporaryDirectory() as tmpdirname: + temporary_path = lambda path: path_join(tmpdirname, basename(path)) - run_command( - CC, - *command_line_split(CFLAGS), - INPUT_FILE, - PATHS["fwrite_le"], - "-o", OUTPUT_FILE, - "-I" + PATHS["include_directory"] - ) - run_command(OUTPUT_FILE) + substitute_temp = temporary_path(INPUT_FILE) + rewrite_file(substitute_temp, substitute_vars({ + "bytebeat_contents": bytebeat_contents, + "output_file": C_str_repr(args.output), + "sample_rate": \ + value if (value := args.final_sample_rate) else \ + args.sample_rate, + "original_sample_rate": original_sample_rate, + "final_sample_rate_code": final_sample_rate_code, + "bit_depth": args.bit_depth, + "is_signed": args.signed, + "precalculated_ratio": args.precalculate_ratio, + "faster_sample_ratio_math": args.precalculate_ratio, + "fp_return_type": args.floating_point, + "channels": args.channels, + "length": samples, + "wav_product": gen_length * (args.bit_depth // BITS_PER_BYTE), + "gen_length": gen_length, + "sequential_mode": args.mode == "sequential", + "block_size": args.block_size, + "silent_mode": args.silent, + "verbose_mode": args.verbose and not args.silent, + "fwrite_le": PATHS["fwrite_le_header"], + "ansi_escape_codes_supported": ansi_escape_codes_supported + }, read_file(PATHS["template"]), args.show_substituted_values)) - if not args.keep_files: - delete_file(PATHS["substitute"]) - delete_file(OUTPUT_FILE) - delete_empty_dir(PATHS["bin_dir"]) + # Compile + print("Compiling") + + output_file_temp = temporary_path(OUTPUT_FILE) + + run_command( + CC, + *command_line_split(CFLAGS), + substitute_temp, + PATHS["fwrite_le"], + "-o", output_file_temp, + "-I" + PATHS["include_directory"] + ) + run_command(output_file_temp) + + if args.keep_files: + makedirs(PATHS["bin_dir"], exist_ok=True) + + rename(substitute_temp, INPUT_FILE) + rename(output_file_temp, OUTPUT_FILE)