import os import signal import subprocess import time from .build import Build from .config import Config from .logger import Logger from .storage import Storage def create_builds_table(conn): with conn: conn.execute(""" CREATE TABLE IF NOT EXISTS builds ( ctk TEXT NOT NULL, cccl TEXT NOT NULL, bench TEXT NOT NULL, code TEXT NOT NULL, elapsed REAL ); """) class CMakeCache: _instance = None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls, *args, **kwargs) create_builds_table(Storage().connection()) return cls._instance def pull_build(self, bench): config = Config() ctk = config.ctk cccl = config.cccl conn = Storage().connection() with conn: query = "SELECT code, elapsed FROM builds WHERE ctk = ? AND cccl = ? AND bench = ?;" result = conn.execute(query, (ctk, cccl, bench.label())).fetchone() if result: code, elapsed = result return Build(int(code), float(elapsed)) return result def push_build(self, bench, build): config = Config() ctk = config.ctk cccl = config.cccl conn = Storage().connection() with conn: conn.execute( "INSERT INTO builds (ctk, cccl, bench, code, elapsed) VALUES (?, ?, ?, ?, ?);", (ctk, cccl, bench.label(), build.code, build.elapsed), ) class CMake: def __init__(self): pass def do_build(self, bench, timeout): logger = Logger() try: if not bench.is_base(): with open(bench.exe_name() + ".h", "w") as f: f.writelines(bench.definitions()) cmd = ["cmake", "--build", ".", "--target", bench.exe_name()] logger.info( "starting build for {}: {}".format(bench.label(), " ".join(cmd)) ) begin = time.time() p = subprocess.Popen( cmd, start_new_session=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) p.wait(timeout=timeout) elapsed = time.time() - begin logger.info( "finished build for {} (exit code: {}) in {:.3f}s".format( bench.label(), p.returncode, elapsed ) ) return Build(p.returncode, elapsed) except subprocess.TimeoutExpired: logger.info( "build for {} reached timeout of {}s".format(bench.label(), timeout) ) os.killpg(os.getpgid(p.pid), signal.SIGTERM) return Build(424242, float("inf")) def build(self, bench): logger = Logger() timeout = None cache = CMakeCache() if bench.is_base(): # Only base build can be pulled from cache build = cache.pull_build(bench) if build: logger.info("found cached base build for {}".format(bench.label())) if bench.is_base(): if not os.path.exists("bin/{}".format(bench.exe_name())): self.do_build(bench, None) return build else: base_build = self.build(bench.get_base()) if base_build.code != 0: raise Exception("Base build failed") timeout = base_build.elapsed * 10 build = self.do_build(bench, timeout) cache.push_build(bench, build) return build def clean(): cmd = ["cmake", "--build", ".", "--target", "clean"] p = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) p.wait() if p.returncode != 0: raise Exception("Unable to clean build directory")