diff --git a/configs/bridge/bridge.json b/configs/bridge/bridge.json new file mode 100644 index 0000000..6a6b926 --- /dev/null +++ b/configs/bridge/bridge.json @@ -0,0 +1,14 @@ +{ + "Benchmark Launcher": { + "tool": "CPAchecker" + }, + "uploader": { + "identifier": "", + "upload results": true, + "parent id": true, + "server": "", + "user": "", + "password": "", + "name": "runner " + } +} diff --git a/configs/bridge/job.json b/configs/bridge/job.json new file mode 100644 index 0000000..bf47728 --- /dev/null +++ b/configs/bridge/job.json @@ -0,0 +1,12 @@ +{ + "project": "Linux", + "targets": [ + "drivers/net/**", + "drivers/usb/**" + ], + "specifications set": "5.5", + "requirement specifications": [ + "memory safety with mea", + "concurrency safety" + ] +} diff --git a/configs/bridge/launch.json b/configs/bridge/launch.json new file mode 100644 index 0000000..aebea16 --- /dev/null +++ b/configs/bridge/launch.json @@ -0,0 +1,59 @@ +{ + "code coverage details": "2", + "collect total code coverage": true, + "ignore failed sub-jobs": false, + "ignore other instances": false, + "keep intermediate files": true, + "logging": { + "formatters": [ + { + "name": "brief", + "value": "%(asctime)s (%(filename)s:%(lineno)03d) %(name)s %(levelname)5s> %(message)s" + }, + { + "name": "detailed", + "value": "%(asctime)s (%(filename)s:%(lineno)03d) %(name)s %(levelname)5s> %(message)s" + } + ], + "loggers": [ + { + "handlers": [ + { + "formatter": "brief", + "level": "NONE", + "name": "console" + }, + { + "formatter": "detailed", + "level": "NONE", + "name": "file" + } + ], + "name": "default" + } + ] + }, + "max solving tasks per sub-job": 10000, + "parallelism": { + "Sub-jobs processing": 1, + "EMG": 1.0, + "Plugins": 0.75, + "Weaving": 0.5, + "Results processing": 0.25 + }, + "priority": "LOW", + "resource limits": { + "CPU model": null, + "CPU time for executed commands": 450, + "disk memory size": 500000000000, + "CPU time for EMG": 450, + "memory size": 1000000000, + "memory size for executed commands": 2000000000, + "memory size for EMG": 2000000000, + "number of CPU cores": null + }, + "task scheduler": "Klever", + "upload verifier input files": false, + "upload other intermediate files": false, + "weight": "1" +} diff --git a/configs/bridge/resource.json b/configs/bridge/resource.json new file mode 100644 index 0000000..1154920 --- /dev/null +++ b/configs/bridge/resource.json @@ -0,0 +1,9 @@ +{ + "CPU time": "5min", + "wall time": "7min", + "soft CPU time": 0.9, + "memory size": "5GB", + "CPU model": null, + "number of CPU cores": 0, + "disk memory size": "1GB" +} diff --git a/configs/bridge/runner.json b/configs/bridge/runner.json new file mode 100644 index 0000000..fced104 --- /dev/null +++ b/configs/bridge/runner.json @@ -0,0 +1,26 @@ +{ + "Builder": { + "cif": "", + "version": "5.10.193", + "work dir": "/ssd/build_base", + "cache ": "/ssd/build_base/build-base-linux-5.10.193-allmodconfig" + }, + "Klever": { + "user": "admin", + "pass": "admin", + "host": "", + "home dir": "", + "launch config": "launch.json", + "job config": "job.json", + "resource config": "resource.json", + "verifier options": "verifier_options.json", + "job id": "", + "python-venv": "/usr/local/python3.10-klever/bin/python3", + "deploy dir": "" + }, + "Bridge": { + "home dir": "", + "bridge config": "bridge.json", + "work dir": "" + } +} diff --git a/configs/bridge/verifier_options.json b/configs/bridge/verifier_options.json new file mode 100644 index 0000000..dc69f05 --- /dev/null +++ b/configs/bridge/verifier_options.json @@ -0,0 +1,203 @@ +{ + "templates": { + "CPAchecker common": { + "description": "Common options for the CPAchecker tool", + "add options": [ + {"-setprop": "cpa.callstack.unsupportedFunctions=__VERIFIER_nonexisting_dummy_function"}, + {"-setprop": "cpa.predicate.allowedUnsupportedFunctions=memset,memcpy,__builtin_add_overflow,__builtin_mul_overflow,__builtin_va_arg"}, + {"-setprop": "cpa.value.allowedUnsupportedFunctions=memset,memcpy,__builtin_add_overflow,__builtin_mul_overflow,__builtin_va_arg"}, + {"-setprop": "counterexample.export.extendedWitnessFile=witness.%d.graphml"}, + {"-setprop": "counterexample.export.exportExtendedWitness=true"}, + {"-setprop": "counterexample.export.compressWitness=false"}, + {"-setprop": "cpa.arg.witness.removeInsufficientEdges=false"}, + {"-setprop": "counterexample.export.exportCounterexampleCoverage=true"}, + {"-setprop": "counterexample.export.prefixAdditionalCoverageFile=Counterexample.%d.additionalCoverage.info"}, + {"-setprop": "additionalCoverage.file=additionalCoverage.info"}, + {"-setprop": "parser.readLineDirectives=true"}, + {"-setprop": "cpa.arg.proofWitness=witness.correctness.graphml"}, + {"-setprop": "cpa.arg.export=true"}, + {"-setprop": "cpa.arg.compressWitness=false"}, + {"-noout": ""}, + {"-setprop": "shutdown.timeout=100"}, + {"-heap": "%ldv:memory size:0.87:MB%m"} + ], + "architecture dependant options": { + "x86-64": {"add options": [{"-64": ""}]}, + "ARM": {"add options": [{"-setprop": "analysis.machineModel=ARM"}]}, + "ARM64": {"add options": [{"-setprop": "analysis.machineModel=ARM64"}]} + } + }, + "Ultimate common": { + "description": "Common options for the UltimateAutimizer tool", + "add options": [ + {"--witness-name": "witness.1.graphml"}, + {"--witness-dir": "./output/"}, + {"--architecture": "64bit"} + ] + }, + "CPALockator races": { + "description": "Common part of CPAchecker configuration for checking races", + "inherit": "CPAchecker common", + "safety properties": ["CHECK( init({entry_point}()), LTL(G ! data-race) )"], + "add options": [{"-setprop": "counterexample.export.graphml=witness.%d.graphml"}] + }, + "CPAchecker reachability": { + "description": "CPAchecker for reachability checking", + "inherit": "CPAchecker common", + "safety properties": ["CHECK( init({entry_point}()), LTL(G ! call(__VERIFIER_error())) )"], + "add options": [{"-ldv": ""}] + }, + "CPAchecker BAM reachability": { + "description": "CPAchecker with BAM for reachability checking", + "inherit": "CPAchecker common", + "safety properties": ["CHECK( init({entry_point}()), LTL(G ! call(__VERIFIER_error())) )"], + "add options": [ + {"-setprop": "counterexample.export.allowImpreciseCounterexamples=false"}, + {"-ldv-bam": ""} + ] + }, + "CPAchecker BAM reachability FP": { + "description": "CPAchecker with BAM and FPA for reachability checking", + "inherit": "CPAchecker BAM reachability", + "add options": [ + {"-setprop": "CompositeCPA.cpas=cpa.location.LocationCPA,cpa.callstack.CallstackCPA,cpa.value.ValueAnalysisCPA,cpa.predicate.BAMPredicateCPA"}, + {"-setprop": "cpa.value.ignoreFunctionValue=false"} + ] + }, + "CPAchecker BAM reachability bit-precise": { + "description": "CPAchecker with bit-precise BAM for reachability checking", + "inherit": "CPAchecker BAM reachability", + "add options": [ + {"-setprop": "cpa.predicate.encodeBitvectorAs=BITVECTOR"}, + {"-setprop": "solver.solver=MathSAT5"} + ] + }, + "CPAchecker BAM reachability heap arrays": { + "description": "CPAchecker with BAM and heap arrays for reachability checking", + "inherit": "CPAchecker BAM reachability", + "add options": [ + {"-setprop": "cpa.predicate.useArraysForHeap=true"}, + {"-setprop": "cpa.predicate.defaultArrayLength=20"}, + {"-setprop": "cpa.predicate.maxArrayLength=-1"} + ] + }, + "CPAchecker BAM BusyBox": { + "description": "CPAchecker with BAM for reachability checking and FPA", + "inherit": "CPAchecker BAM reachability FP", + "add options": [ + {"-setprop": "cpa.predicate.defaultArrayLength=5"}, + {"-setprop": "cpa.predicate.maxArrayLength=5"} + ] + }, + "CPAchecker SMG memory checking": { + "description": "CPAchecker with SMG for memory errors checking", + "inherit": "CPAchecker common", + "safety properties": [ + "CHECK( init({entry_point}()), LTL(G valid-free) )", + "CHECK( init({entry_point}()), LTL(G valid-deref) )", + "CHECK( init({entry_point}()), LTL(G valid-memtrack) )" + ], + "add options": [ + {"-smg-ldv": ""}, + {"-setprop": "CompositeCPA.cpas=cpa.location.LocationCPA,cpa.callstack.CallstackCPA,cpa.smg.SMGCPA"}, + {"-setprop": "cpa.smg.memcpyFunctions=__VERIFIER_memcpy"}, + {"-setprop": "cpa.smg.memsetFunctions=__VERIFIER_memset"} + ] + }, + "CPAchecker SMG memory checking with mea": { + "description": "CPAchecker with SMG for memory errors checking", + "inherit": "CPAchecker SMG memory checking", + "add options": [ + {"-setprop": "cpa.arg.witness.handleTMPVariableAsEpsilonForWitness=false"}, + {"-setprop": "counterexample.export.graphml="}, + {"-setprop": "counterexample.export.extendedWitnessFile=witness.%d.graphml"}, + {"-setprop": "counterexample.export.exportExtendedWitness=true"}, + {"-setprop": "analysis.stopAfterError=false"}, + {"-setprop": "counterexample.export.exportImmediately=true"}, + {"-setprop": "counterexample.export.filters=PathEqualityCounterexampleFilter"}, + {"-setprop": "analysis.algorithm.CEGAR=true"}, + {"-setprop": "cegar.refiner=cpa.arg.AbstractARGBasedRefiner"} + ] + }, + "CPAchecker SMG without support of uncertain environment behavior": { + "description": "CPAchecker with SMG for memory errors checking that almost does not support any uncertainty in behavior of environment", + "inherit": "CPAchecker SMG memory checking", + "add options": [ + {"-setprop": "cpa.smg.handleIncompleteExternalVariableAsExternalAllocation=false"}, + {"-setprop": "cpa.smg.handleUnknownDereferenceAsSafe=false"}, + {"-setprop": "cpa.smg.handleUnknownFunctions=STRICT"}, + {"-setprop": "cpa.smg.produceErrorTraceInsteadOfException=true"}, + {"-setprop": "cpa.smg.safeUnknownFunctionsPatterns=ldv_.*,printk,schedule"} + ] + }, + "CPAchecker SMG without deducing abstraction for lists": { + "description": "CPAchecker with SMG for memory errors checking that does not spend time for deducing abstraction for lists (this should unlikely be used for target programs with lists)", + "inherit": "CPAchecker SMG memory checking", + "add options": [{"-setprop": "cpa.smg.enableHeapAbstraction=false"}] + }, + "CPALockator base": { + "description": "CPAchecker for checking races", + "inherit": "CPALockator races", + "add options": [{"-lockator-linux-pre-shared-ref": ""}] + }, + "CPALockator lightweight": { + "description": "Lightweight CPAchecker for checking races", + "inherit": "CPALockator races", + "add options": [{"-lockator-linux-lightweight": ""}] + }, + "CPALockator thread-modular": { + "description": "CPAchecker for checking races with powerful theory", + "inherit": "CPALockator races", + "add options": [{"-lockator-threadmodular-linux": ""}] + }, + "CPALockator rcu": { + "description": "CPAchecker for checking races over rcu pointers", + "inherit": "CPALockator races", + "add options": [{"-rcucpa": ""}] + } + }, + "profiles": { + "reachability": { + "CPAchecker": { + "smg-master:c6f6a66": {"inherit": "CPAchecker BAM reachability"} + }, + "UltimateAutomizer": {"v0.1.20": {"inherit": "Ultimate common"}} + }, + "reachability with function pointers": { + "CPAchecker": {"smg-master:c6f6a66": {"inherit": "CPAchecker BAM reachability FP"}} + }, + "reachability with bit precision": { + "CPAchecker": {"smg-master:c6f6a66": {"inherit": "CPAchecker BAM reachability bit-precise"}} + }, + "reachability with heap arrays": { + "CPAchecker": {"smg-master:c6f6a66": {"inherit": "CPAchecker BAM reachability heap arrays"}} + }, + "CPAchecker BAM BusyBox": { + "CPAchecker": {"smg-master:c6f6a66": {"inherit": "CPAchecker BAM BusyBox"}} + }, + "race checking base": { + "CPAchecker": {"CPALockator-update:39383": {"inherit": "CPALockator base"}} + }, + "race checking lightweight": { + "CPAchecker": {"CPALockator-update:39383": {"inherit": "CPALockator lightweight"}} + }, + "race checking": { + "CPAchecker": {"CPALockator-update:39383": {"inherit": "CPALockator thread-modular"}} + }, + "race checking rcu": { + "CPAchecker": {"CPALockator-update:39383": {"inherit": "CPALockator rcu"}} + }, + "memory checking": { + "CPAchecker": {"smg-master:c6f6a66": {"inherit": "CPAchecker SMG memory checking"}} + }, + "memory checking with mea": { + "CPAchecker": {"smg-master:c6f6a66": {"inherit": "CPAchecker SMG memory checking with mea"}} + }, + "memory checking without uncertainty": { + "CPAchecker": {"smg-master:c6f6a66": {"inherit": "CPAchecker SMG without support of uncertain environment behavior"}} + }, + "memory checking without abstraction for lists": { + "CPAchecker": {"smg-master:c6f6a66": {"inherit": "CPAchecker SMG without deducing abstraction for lists"}} + } + } +} diff --git a/scripts/runner.py b/scripts/runner.py new file mode 100644 index 0000000..173c849 --- /dev/null +++ b/scripts/runner.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 + +# +# CV is a framework for continuous verification. +# +# Copyright (c) 2018-2023 ISP RAS (http://www.ispras.ru) +# Ivannikov Institute for System Programming of the Russian Academy of Sciences +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +""" +Klever runner - creates a build base, launches Klever and converts results to CVV format. +""" + +import json +import os +import re +import shutil +import sys +import time +import argparse + +from components.component import Component +from components import * + + +TAG_BRIDGE = "Bridge" +TAG_KLEVER = "Klever" +TAG_BUILDER = "Builder" +TAG_INSTALL_DIR = "install dir" +TAG_VERSION = "version" +TAG_CIF = "cif" +TAG_WORK_DIR = "work dir" +TAG_CACHE = "cache" +TAG_HOME_DIR = "home dir" +TAG_ARCH = "architecture" +TAG_KLEVER_HOST = "host" +TAG_KLEVER_USER = "user" +TAG_KLEVER_PASS = "pass" +TAG_LAUNCH_CONFIG = "launch config" +TAG_JOB_CONFIG = "job config" +TAG_RESOURCE_CONFIG = "resource config" +TAG_VERIFIER_OPTIONS_CONFIG = "verifier options" +TAG_JOB_ID = "job id" +TAG_PYTHON_VENV = "python-venv" +TAG_BRIDGE_CONFIG = "bridge config" +TAG_DEPLOY_DIR = "deploy dir" +TAG_BUILD_BASE = "build base" +TAG_OUTPUT_DIR = "output dir" +TAG_TASKS_DIR = "tasks dir" + +DEFAULT_MAKE_COMMAND = "allmodconfig" +DEFAULT_ARCH = "x86_64" +BUILDER_SCRIPT = os.path.join("klever", "deploys", "builder.sh") +KLEVER_LAUNCH_SCRIPT = "klever-start-solution" +KLEVER_CHECK_SCRIPT = "klever-download-progress" +DEFAULT_VENV_PATH = "venv/bin" +KLEVER_PROGRESS_FILE = ".klever_progress.json" +BRIDGE_SCRIPT = os.path.join("scripts", "bridge.py") +KLEVER_BUILD_BASE_DIR = "build bases" +COMPONENT_RUNNER = "Runner" +KLEVER_TASKS_DIR = os.path.join("klever-work", "native-scheduler", "scheduler", "tasks") +BUILD_BASE_STORAGE_DIR = "Storage" + +BIG_WAIT_INTERVAL = 100 +SMALL_WAIT_INTERVAL = 10 + + +class Runner(Component): + def __init__(self, general_config: dict): + + super().__init__(COMPONENT_RUNNER, general_config) + bridge_config = self.config[TAG_BRIDGE] + klever_config = self.config[TAG_KLEVER] + builder_config = self.config[TAG_BUILDER] + + # Builder config + self.linux_version = builder_config.get(TAG_VERSION, None) + self.cif_path = self.__normalize_dir(builder_config.get(TAG_CIF, "")) + self.builder_work_dir = self.__normalize_dir(builder_config.get(TAG_WORK_DIR, "")) + self.build_base_cached = self.__normalize_dir(builder_config.get(TAG_CACHE, "")) + self.arch = builder_config.get(TAG_ARCH, DEFAULT_ARCH) + self.make_cmd = builder_config.get(TAG_MAKE_COMMAND, DEFAULT_MAKE_COMMAND) + + # Klever config + self.klever_home_dir = self.__normalize_dir(klever_config.get(TAG_HOME_DIR), TAG_HOME_DIR) + self.klever_deploy_dir = self.__normalize_dir(klever_config.get(TAG_DEPLOY_DIR), TAG_DEPLOY_DIR) + self.launch_config = self.__normalize_dir(klever_config.get(TAG_LAUNCH_CONFIG), TAG_LAUNCH_CONFIG) + self.verifier_options_config = self.__normalize_dir(klever_config.get(TAG_VERIFIER_OPTIONS_CONFIG), + TAG_VERIFIER_OPTIONS_CONFIG) + self.klever_host = klever_config.get(TAG_KLEVER_HOST) + self.klever_user = klever_config.get(TAG_KLEVER_USER) + self.klever_pass = klever_config.get(TAG_KLEVER_PASS) + self.klever_job_id = klever_config.get(TAG_JOB_ID) + self.launch_config = self.__normalize_dir(klever_config.get(TAG_LAUNCH_CONFIG), TAG_LAUNCH_CONFIG) + self.job_config = self.__normalize_dir(klever_config.get(TAG_JOB_CONFIG), TAG_JOB_CONFIG) + self.resource_config = self.__normalize_dir(klever_config.get(TAG_RESOURCE_CONFIG), TAG_RESOURCE_CONFIG) + self.python_venv = self.__normalize_dir(klever_config.get(TAG_PYTHON_VENV, "")) + + # Klever Bridge config + self.bridge_dir = self.__normalize_dir(bridge_config.get(TAG_HOME_DIR), TAG_HOME_DIR) + self.bridge_config = self.__normalize_dir(bridge_config.get(TAG_BRIDGE_CONFIG), TAG_BRIDGE_CONFIG) + self.jobs_dir = self.__normalize_dir(bridge_config.get(TAG_WORK_DIR, "")) + + @staticmethod + def __normalize_dir(dirname: str, fail_with_text="") -> str: + if dirname: + if os.path.exists(dirname): + return os.path.abspath(dirname) + sys.exit(f"Name '{dirname}' does not exist") + else: + if fail_with_text: + sys.exit(f"Name '{fail_with_text}' was not specified") + return "" + + def builder(self) -> str: + """ + Create a build base for specific + """ + if self.build_base_cached: + self.logger.info(f"Reusing build base from {self.build_base_cached}") + return self.build_base_cached + self.logger.info("Preparing build base") + builder_script = os.path.join(self.klever_home_dir, BUILDER_SCRIPT) + cmd = f"{builder_script} --cif {self.cif_path} --version {self.linux_version} " \ + f"--workdir {self.builder_work_dir} --arch {self.arch} --make {self.make_cmd}" + if self.command_caller(cmd): + sys.exit("Cannot build Linux kernel") + build_base_dir = os.path.join(self.builder_work_dir, + f"build-base-linux-{self.linux_version}-{self.arch}-{self.make_cmd}") + self.logger.info(f"Build base has been prepared in {build_base_dir}") + return build_base_dir + + def __update_job_config(self, build_base_dir: str): + with open(self.job_config, errors='ignore', encoding='ascii') as f_jconfig: + job_config = json.load(f_jconfig) + with open(self.bridge_config, errors='ignore', encoding='ascii') as f_bconfig: + bridge_config = json.load(f_bconfig) + build_base_dir_name = os.path.basename(build_base_dir) + dst_build_base_dir = os.path.join(self.klever_deploy_dir, KLEVER_BUILD_BASE_DIR, build_base_dir_name) + if os.path.islink(dst_build_base_dir): + os.unlink(dst_build_base_dir) + os.symlink(build_base_dir, dst_build_base_dir) + job_config[TAG_BUILD_BASE] = build_base_dir_name + bridge_config[COMPONENT_BENCHMARK_LAUNCHER][TAG_OUTPUT_DIR] = \ + os.path.join(self.klever_deploy_dir, KLEVER_TASKS_DIR) + bridge_config[COMPONENT_BENCHMARK_LAUNCHER][TAG_TASKS_DIR] = \ + os.path.join(dst_build_base_dir, BUILD_BASE_STORAGE_DIR) + with open(self.job_config, 'w') as f_jconfig: + json.dump(job_config, f_jconfig, sort_keys=True, indent=4) + with open(self.bridge_config, 'w') as f_bconfig: + json.dump(bridge_config, f_bconfig, sort_keys=True, indent=4) + + def klever(self, build_base_dir: str) -> str: + """ + Create a new Klever job and launch it. + """ + def clear_klever_resources(): + if os.path.exists(KLEVER_PROGRESS_FILE): + os.unlink(KLEVER_PROGRESS_FILE) + self.logger.info("Launching Klever tool") + wall_time_start = time.time() + self.__update_job_config(build_base_dir) + credentials = f"--host {self.klever_host} --username {self.klever_user} --password {self.klever_pass}" + replacement = f"{{\"job.json\": \"{self.job_config}\", \"tasks.json\": \"{self.resource_config}\"," \ + f"\"verifier profiles.json\": \"{self.verifier_options_config}\"}}" + cmd = f"{KLEVER_LAUNCH_SCRIPT} {credentials} --rundata {self.launch_config} " \ + f"--replacement '{replacement}' {self.klever_job_id}" + if self.python_venv: + os.chdir(self.klever_home_dir) + if self.command_caller(f"{self.python_venv} -m venv venv"): + sys.exit("Cannot use python venv") + sys.path.insert(1, os.path.abspath(DEFAULT_VENV_PATH)) + os.environ["PATH"] += os.pathsep + os.path.abspath(DEFAULT_VENV_PATH) + result = self.command_caller_with_output(cmd) + if not result: + sys.exit("Cannot launch Klever") + m = re.search(r': (.+)', result) + if not m: + sys.exit(f"Cannot obtain new job id from output '{result}'") + new_job_id = m.group(1) + + # Wait until job is finished + time.sleep(BIG_WAIT_INTERVAL) + clear_klever_resources() + while True: + cmd = f"{KLEVER_CHECK_SCRIPT} {credentials} -o {KLEVER_PROGRESS_FILE} {new_job_id}" + if self.command_caller(cmd): + sys.exit("Cannot obtain Klever job progress") + with open(KLEVER_PROGRESS_FILE, errors='ignore', encoding='ascii') as f_progress: + job_progress = json.load(f_progress) + if int(job_progress['status']) > 2: + break + time.sleep(SMALL_WAIT_INTERVAL) + clear_klever_resources() + wall_time_start = round(time.time() - wall_time_start, 3) + self.logger.info(f"Klever has been successfully completed in {wall_time_start}s") + return new_job_id + + def bridge(self, new_job_id: str): + """ + Run Klever Bridge and export solved job into CVV. + """ + self.logger.info("Exporting results to CVV format via Klever Bridge") + os.chdir(self.bridge_dir) + cmd = f"{BRIDGE_SCRIPT} -c {self.bridge_config} -j {new_job_id}" + self.logger.debug(f"Run Klever Bridge with {cmd}") + if self.command_caller(cmd): + sys.exit(f"Cannot export results via Klever Bridge. Reproduce with {cmd}") + if self.jobs_dir: + self.logger.info("Clear job files") + shutil.rmtree(os.path.join(self.jobs_dir, new_job_id)) + + def run(self): + build_base_dir = self.builder() + new_job_id = self.klever(build_base_dir) + self.bridge(new_job_id) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("-c", "--config", help="config file", required=True) + + options = parser.parse_args() + with open(options.config, errors='ignore', encoding='ascii') as data_file: + config = json.load(data_file) + + runner = Runner(config) + runner.run()