#! /usr/bin/env python3 # This script regenerates TrustInSoft CI configuration. # Run from the root of the JSON-C project: # $ python3 trustinsoft/regenerate.py import re # sub import json # dumps, load import os # path, makedirs import binascii # hexlify import shutil # copyfileobj import glob # iglob import argparse # ArgumentParser, add_argument, parse_args # Outputting JSON. def string_of_json(obj): # Output standard pretty-printed JSON (RFC 7159) with 4-space indentation. s = json.dumps(obj, indent=4) # Sometimes we need to have multiple "include" fields in the outputted # JSON, which is unfortunately impossible in the internal python # representation (OK, it is technically possible, but too cumbersome to # bother implementing it here), so we can name these fields 'include_', # 'include__', etc, and they are all converted to 'include' before # outputting as JSON. s = re.sub(r'"include_+"', '"include"', s) return s # --------------------------------------------------------------------------- # # ----------------------------- PARSE ARGUMENTS ----------------------------- # # --------------------------------------------------------------------------- # parser = argparse.ArgumentParser( description="Regenerate the TrustInSoft CI files.", epilog="Please call this script only after building JSON-C.") parser.add_argument("build_dir", default="json-c-build", help="path to the build directory where cmake was called") parser.add_argument("--fuzz-tests", default="all", choices=["none", "some", "all"], help="include none / some / all fuzz tests in tis.config \ (default is 'all')") parser.add_argument("--safe-arrays", action="store_true", help="for multidimensional arrays or arrays that are \ fields inside structs, assume that accesses are in \ bounds") args = parser.parse_args() fuzz_tests = args.fuzz_tests build_dir = args.build_dir safe_arrays = args.safe_arrays # Generated files which need to be a part of the repository. def make_simple_copy_file(filename): return ( { "src": os.path.join(build_dir, filename), "dst": os.path.join("trustinsoft", "include", filename), } ) files_to_copy = [ make_simple_copy_file("apps_config.h"), make_simple_copy_file("config.h"), make_simple_copy_file("json_config.h"), ] # --------------------------------------------------------------------------- # # ---------------------------------- CHECKS --------------------------------- # # --------------------------------------------------------------------------- # def check_dir(dir): if os.path.isdir(dir): print(" > OK! Directory '%s' exists." % dir) else: exit("Directory '%s' not found." % dir) def check_file(file): if os.path.isfile(file): print(" > OK! File '%s' exists." % file) else: exit("File '%s' not found." % file) # Initial check. print("1. Check if all necessary directories and files exist...") check_dir("trustinsoft") check_dir(build_dir) for file in files_to_copy: check_file(file['src']) # --------------------------------------------------------------------------- # # -------------------- GENERATE trustinsoft/common.config ------------------- # # --------------------------------------------------------------------------- # common_config_path = os.path.join("trustinsoft", "common.config") def string_of_options(options): elts = [] for opt_prefix in options: # e.g. opt_prefix == "-D" for opt_value in options[opt_prefix]: # e.g. opt_value == "HAVE_OPEN" elts.append(opt_prefix + opt_value) # e.g. "-DHAVE_OPEN" return " ".join(elts) def make_common_config(): # C files. c_files = ( sorted(glob.iglob("*.c", recursive=False)) + [ os.path.join("tests", "parse_flags.c") ] ) # Compilation options. compilation_cmd = ( { "-I": [ "include", "..", os.path.join("..", "tests"), ], "-D": [], "-U": [], } ) # Whole common.config JSON. config = ( { "files": list(map(lambda file: os.path.join("..", file), c_files)), "compilation_cmd": string_of_options(compilation_cmd), "val-printf-singleton-pointers": True, "val-int-for-pointer-equality": [ "-1", "-2" ] } ) # The "safe-arrays" field if not default (i.e. not true). if not safe_arrays: config["safe-arrays"] = False # Done. return config common_config = make_common_config() with open(common_config_path, "w") as file: print("2. Generate the 'trustinsoft/common.config' file.") file.write(string_of_json(common_config)) # --------------------------------------------------------------------------- # # -------------------------------- tis.config ------------------------------- # # --------------------------------------------------------------------------- # tests = ( [ { "test": "test1", "formatted": False, }, { "test": "test1", "formatted": True, "args": [ "plain" ], }, { "test": "test1", "formatted": True, "args": [ "pretty" ], }, { "test": "test1", "formatted": True, "args": [ "spaced" ], }, { "test": "test1", "formatted": True, "args": [ "spaced", "pretty" ], }, { "test": "test1", "formatted": True, "args": [ "spaced", "pretty", "pretty_tab" ], }, { "test": "test2", "formatted": False, }, { "test": "test2", "formatted": True, "args": [ "plain" ], }, { "test": "test2", "formatted": True, "args": [ "pretty" ], }, { "test": "test2", "formatted": True, "args": [ "spaced" ], }, { "test": "test2", "formatted": True, "args": [ "spaced", "pretty" ], }, { "test": "test2", "formatted": True, "args": [ "spaced", "pretty", "pretty_tab" ], }, { "test": "test4" }, { "test": "test_cast" }, { "test": "test_charcase" }, { "test": "test_compare" }, { "test": "test_deep_copy" }, { "test": "test_double_serializer" }, { "test": "test_float" }, { "test": "test_int_add" }, { "test": "test_json_pointer" }, { "test": "test_locale" }, { "test": "test_null" }, { "test": "test_object_iterator", "args": [ "." ], }, { "test": "test_parse" }, { "test": "test_parse_int64" }, { "test": "test_printbuf" }, { "test": "testReplaceExisting" }, { "test": "test_set_serializer" }, { "test": "test_set_value" }, { "test": "test_util_file", "args": [ "." ], "filesystem": { "files": [ { "from": "tests/valid.json", "name": "./valid.json" }, { "from": "tests/valid_nested.json", "name": "./valid_nested.json" }, { "name": "/dev/null" }, ], }, }, { "test": "test_visit" }, ] ) fuzz_input_dir = os.path.join("trustinsoft", "fuzz_inputs") interesting_fuzz_inputs = frozenset([ "0-10058b8cd9", "0-4735d351ed", "0-638577393e", "1-8e3702d59d", "1-fb0eb4ff8c", ]) def is_fuzz_input_interesting(fuzz_input): if fuzz_tests == "all": return True elif fuzz_tests == "some": return fuzz_input in interesting_fuzz_inputs else: return False def fuzz_inputs(): fuzz_filepaths = sorted( glob.iglob(os.path.join(fuzz_input_dir, "*.json"), recursive=False) ) fuzz_inputs = (list(map( lambda path: (os.path.basename(path)).replace(".json", ""), fuzz_filepaths ))) return list(filter(is_fuzz_input_interesting, fuzz_inputs)) def make_test(test): name_elts = [] if "formatted" in test: if test["formatted"]: name_elts.append("Formatted") if "args" in test: name_elts += test["args"] name = test["test"] + "_".join(name_elts) tis_test = ( { "name": name, "include": common_config_path, } ) if "formatted" in test: if test["formatted"]: compilation_cmd = { "-D": [ "TEST_FORMATTED" ] } else: compilation_cmd = { "-U": [ "TEST_FORMATTED" ] } tis_test["compilation_cmd"] = string_of_options(compilation_cmd) tis_test["files"] = [ os.path.join("tests", test["test"] + ".c") ] if "filesystem" in test: tis_test["filesystem"] = test["filesystem"] if "args" in test: tis_test["val-args"] = " " + " ".join(test["args"]) return tis_test def make_fuzz_test(fuzz_input): fuzz_filename = fuzz_input + ".json" tis_test = ( { "name": ("test_fuzz input " + fuzz_filename), "include": common_config_path, "files": [ os.path.join("trustinsoft", "test_fuzz.c") ], "filesystem": { "files": [ { "from": os.path.join(fuzz_input_dir, fuzz_filename), "name": "test.json" } ] }, "val-args": " test.json" } ) return tis_test tis_config = ( list(map(make_test, tests)) + list(map(make_fuzz_test, fuzz_inputs())) ) with open("tis.config", "w") as file: print("3. Generate the tis.config file.") file.write(string_of_json(tis_config)) # --------------------------------------------------------------------------- # # ------------------------------ COPY .h FILES ------------------------------ # # --------------------------------------------------------------------------- # print("5. Copy generated files.") for file in files_to_copy: with open(file['src'], 'r') as f_src: os.makedirs(os.path.dirname(file['dst']), exist_ok=True) with open(file['dst'], 'w') as f_dst: print(" > Copy '%s' to '%s'." % (file['src'], file['dst'])) shutil.copyfileobj(f_src, f_dst)