You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
6.3 KiB
Python
220 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Write a ninja build file to generate reports for cbmc proofs.
|
|
|
|
Given a list of folders containing cbmc proofs, write a ninja build
|
|
file the generate reports for these proofs. The list of folders may
|
|
be given on the command line, in a json file, or found in the file
|
|
system.
|
|
"""
|
|
|
|
# Add task pool
|
|
|
|
import sys
|
|
import os
|
|
import platform
|
|
import argparse
|
|
import json
|
|
|
|
################################################################
|
|
# The command line parser
|
|
|
|
def argument_parser():
|
|
"""Return the command line parser."""
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description='Generate ninja build file for cbmc proofs.',
|
|
epilog="""
|
|
Given a list of folders containing cbmc proofs, write a ninja build
|
|
file the generate reports for these proofs. The list of folders may
|
|
be given on the command line, in a json file, or found in the file
|
|
system.
|
|
In the json file, there should be a dict mapping the key "proofs"
|
|
to a list of folders containing proofs.
|
|
The file system, all folders folders under the current directory
|
|
containing a file named 'cbmc-batch.yaml' is considered a
|
|
proof folder.
|
|
This script assumes that the proof is driven by a Makefile
|
|
with targets goto, cbmc, coverage, property, and report.
|
|
This script does not work with Windows and Visual Studio.
|
|
"""
|
|
)
|
|
parser.add_argument('folders', metavar='PROOF', nargs='*',
|
|
help='Folder containing a cbmc proof')
|
|
parser.add_argument('--proofs', metavar='JSON',
|
|
help='Json file listing folders containing cbmc proofs')
|
|
return parser
|
|
|
|
################################################################
|
|
# The list of folders containing proofs
|
|
#
|
|
# The list of folders will be taken from
|
|
# 1. the list PROOFS defined here or
|
|
# 2. the command line
|
|
# 3. the json file specified on the command line containing a
|
|
# dict mapping the key JSON_KEY to a list of folders
|
|
# 4. the folders below the current directory containing
|
|
# a file named FS_KEY
|
|
#
|
|
|
|
PROOFS = [
|
|
]
|
|
|
|
JSON_KEY = 'proofs'
|
|
FS_KEY = 'cbmc-batch.yaml'
|
|
|
|
def find_proofs_in_json_file(filename):
|
|
"""Read the list of folders containing proofs from a json file."""
|
|
|
|
if not filename:
|
|
return []
|
|
try:
|
|
with open(filename) as proofs:
|
|
return json.load(proofs)[JSON_KEY]
|
|
except (FileNotFoundError, KeyError):
|
|
raise UserWarning("Can't find key {} in json file {}".format(JSON_KEY, filename))
|
|
except json.decoder.JSONDecodeError:
|
|
raise UserWarning("Can't parse json file {}".format(filename))
|
|
|
|
def find_proofs_in_filesystem():
|
|
"""Locate the folders containing proofs in the filesystem."""
|
|
|
|
proofs = []
|
|
for root, _, files in os.walk('.'):
|
|
if FS_KEY in files:
|
|
proofs.append(os.path.normpath(root))
|
|
return proofs
|
|
|
|
################################################################
|
|
# The strings used to write sections of the ninja file
|
|
|
|
NINJA_RULES = """
|
|
################################################################
|
|
# task pool to force sequential builds of goto binaries
|
|
|
|
pool goto_pool
|
|
depth = 1
|
|
|
|
################################################################
|
|
# proof target rules
|
|
|
|
rule build_goto
|
|
command = make -C ${folder} goto
|
|
pool = goto_pool
|
|
|
|
rule build_cbmc
|
|
command = make -C ${folder} cbmc
|
|
|
|
rule build_coverage
|
|
command = make -C ${folder} coverage
|
|
|
|
rule build_property
|
|
command = make -C ${folder} property
|
|
|
|
rule build_report
|
|
command = make -C ${folder} report
|
|
|
|
rule clean_folder
|
|
command = make -C ${folder} clean
|
|
|
|
rule veryclean_folder
|
|
command = make -C ${folder} veryclean
|
|
|
|
rule open_proof
|
|
command = open ${folder}/html/index.html
|
|
|
|
"""
|
|
|
|
NINJA_BUILDS = """
|
|
################################################################
|
|
# {folder} proof targets
|
|
|
|
build {folder}/{entry}.goto: build_goto
|
|
folder={folder}
|
|
|
|
build {folder}/cbmc.txt: build_cbmc {folder}/{entry}.goto
|
|
folder={folder}
|
|
|
|
build {folder}/coverage.xml: build_coverage {folder}/{entry}.goto
|
|
folder={folder}
|
|
|
|
build {folder}/property.xml: build_property {folder}/{entry}.goto
|
|
folder={folder}
|
|
|
|
build {folder}/html/index.html: build_report {folder}/{entry}.goto {folder}/cbmc.txt {folder}/coverage.xml {folder}/property.xml
|
|
folder={folder}
|
|
|
|
build clean_{folder}: clean_folder
|
|
folder={folder}
|
|
|
|
build veryclean_{folder}: veryclean_folder
|
|
folder={folder}
|
|
|
|
build open_{folder}: open_proof
|
|
folder={folder}
|
|
|
|
build {folder}: phony {folder}/html/index.html
|
|
|
|
default {folder}
|
|
|
|
"""
|
|
|
|
NINJA_GLOBALS = """
|
|
################################################################
|
|
# global targets
|
|
|
|
build clean: phony {clean_targets}
|
|
|
|
build veryclean: phony {veryclean_targets}
|
|
|
|
build open: phony {open_targets}
|
|
"""
|
|
|
|
################################################################
|
|
# The main function
|
|
|
|
def get_entry(folder):
|
|
"""Find the name of the proof in the proof Makefile."""
|
|
|
|
with open('{}/Makefile'.format(folder)) as makefile:
|
|
for line in makefile:
|
|
if line.strip().lower().startswith('entry'):
|
|
return line[line.find('=')+1:].strip()
|
|
if line.strip().lower().startswith('h_entry'):
|
|
return line[line.find('=')+1:].strip()
|
|
raise UserWarning("Can't find ENTRY in {}/Makefile".format(folder))
|
|
|
|
def write_ninja_build_file():
|
|
"""Write a ninja build file to generate proof results."""
|
|
|
|
if platform.system().lower() == 'windows':
|
|
print("This script does not run on Windows.")
|
|
sys.exit()
|
|
|
|
args = argument_parser().parse_args()
|
|
|
|
proofs = (PROOFS or
|
|
args.folders or
|
|
find_proofs_in_json_file(args.proofs) or
|
|
find_proofs_in_filesystem())
|
|
|
|
with open('build.ninja', 'w') as ninja:
|
|
ninja.write(NINJA_RULES)
|
|
for proof in proofs:
|
|
entry = get_entry(proof)
|
|
ninja.write(NINJA_BUILDS.format(folder=proof, entry=entry))
|
|
targets = lambda kind, folders: ' '.join(
|
|
['{}_{}'.format(kind, folder) for folder in folders]
|
|
)
|
|
ninja.write(NINJA_GLOBALS.format(
|
|
clean_targets=targets('clean', proofs),
|
|
veryclean_targets=targets('veryclean', proofs),
|
|
open_targets=targets('open', proofs)
|
|
))
|
|
|
|
################################################################
|
|
|
|
if __name__ == "__main__":
|
|
write_ninja_build_file()
|