2018-07-21 07:00:34 +08:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
#
|
|
|
|
#===- gen_tests.py - clang-doc test generator ----------------*- python -*--===#
|
|
|
|
#
|
|
|
|
# The LLVM Compiler Infrastructure
|
|
|
|
#
|
|
|
|
# This file is distributed under the University of Illinois Open Source
|
|
|
|
# License. See LICENSE.TXT for details.
|
|
|
|
#
|
|
|
|
#===------------------------------------------------------------------------===#
|
|
|
|
"""
|
|
|
|
clang-doc test generator
|
|
|
|
==========================
|
|
|
|
|
|
|
|
Generates tests for clang-doc given a certain set of flags, a prefix for the
|
|
|
|
test file, and a given clang-doc binary. Please check emitted tests for
|
|
|
|
accuracy before using.
|
|
|
|
|
|
|
|
To generate all current tests:
|
|
|
|
- Generate mapper tests:
|
2018-08-17 05:54:34 +08:00
|
|
|
python gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix mapper -use-check-next
|
2018-07-21 07:00:34 +08:00
|
|
|
|
|
|
|
- Generate reducer tests:
|
2018-08-17 05:54:34 +08:00
|
|
|
python gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix bc -use-check-next
|
2018-08-03 04:10:17 +08:00
|
|
|
|
2018-07-21 07:00:34 +08:00
|
|
|
- Generate yaml tests:
|
2018-08-17 05:54:34 +08:00
|
|
|
python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix yaml -use-check-next
|
2018-08-03 04:10:17 +08:00
|
|
|
|
|
|
|
- Generate public decl tests:
|
2018-08-17 05:54:34 +08:00
|
|
|
python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix public -use-check-next
|
|
|
|
|
|
|
|
- Generate Markdown tests:
|
|
|
|
python gen_tests.py -flag='--format=md' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix md
|
2018-08-03 04:10:17 +08:00
|
|
|
|
2018-07-21 07:00:34 +08:00
|
|
|
This script was written on/for Linux, and has not been tested on any other
|
|
|
|
platform and so it may not work.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import glob
|
|
|
|
import os
|
2018-08-03 04:10:17 +08:00
|
|
|
import re
|
2018-07-21 07:00:34 +08:00
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
|
|
|
|
RUN_CLANG_DOC = """
|
|
|
|
// RUN: clang-doc {0} -p %t %t/test.cpp -output=%t/docs
|
|
|
|
"""
|
|
|
|
RUN = """
|
|
|
|
// RUN: {0} %t/{1} | FileCheck %s --check-prefix CHECK-{2}
|
|
|
|
"""
|
|
|
|
|
|
|
|
CHECK = '// CHECK-{0}: '
|
|
|
|
|
|
|
|
CHECK_NEXT = '// CHECK-{0}-NEXT: '
|
|
|
|
|
2018-08-03 04:10:17 +08:00
|
|
|
BITCODE_USR = '<USR abbrevid=4 op0=20 op1={{[0-9]+}} op2={{[0-9]+}} op3={{[0-9]+}} op4={{[0-9]+}} op5={{[0-9]+}} op6={{[0-9]+}} op7={{[0-9]+}} op8={{[0-9]+}} op9={{[0-9]+}} op10={{[0-9]+}} op11={{[0-9]+}} op12={{[0-9]+}} op13={{[0-9]+}} op14={{[0-9]+}} op15={{[0-9]+}} op16={{[0-9]+}} op17={{[0-9]+}} op18={{[0-9]+}} op19={{[0-9]+}} op20={{[0-9]+}}/>'
|
|
|
|
BITCODE_USR_REGEX = r'<USR abbrevid=4 op0=20 op1=[0-9]+ op2=[0-9]+ op3=[0-9]+ op4=[0-9]+ op5=[0-9]+ op6=[0-9]+ op7=[0-9]+ op8=[0-9]+ op9=[0-9]+ op10=[0-9]+ op11=[0-9]+ op12=[0-9]+ op13=[0-9]+ op14=[0-9]+ op15=[0-9]+ op16=[0-9]+ op17=[0-9]+ op18=[0-9]+ op19=[0-9]+ op20=[0-9]+/>'
|
|
|
|
YAML_USR = "USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}'"
|
|
|
|
YAML_USR_REGEX = r"USR: '[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]'"
|
2018-07-21 07:00:34 +08:00
|
|
|
|
|
|
|
def clear_test_prefix_files(prefix, tests_path):
|
|
|
|
if os.path.isdir(tests_path):
|
|
|
|
for root, dirs, files in os.walk(tests_path):
|
|
|
|
for filename in files:
|
|
|
|
if filename.startswith(prefix):
|
|
|
|
os.remove(os.path.join(root, filename))
|
|
|
|
|
|
|
|
|
|
|
|
def copy_to_test_file(test_case_path, test_cases_path):
|
|
|
|
# Copy file to 'test.cpp' to preserve file-dependent USRs
|
|
|
|
test_file = os.path.join(test_cases_path, 'test.cpp')
|
|
|
|
shutil.copyfile(test_case_path, test_file)
|
|
|
|
return test_file
|
|
|
|
|
|
|
|
|
|
|
|
def run_clang_doc(args, out_dir, test_file):
|
|
|
|
# Run clang-doc.
|
|
|
|
current_cmd = [args.clangdoc]
|
|
|
|
current_cmd.extend(args.flags)
|
|
|
|
current_cmd.append('--output=' + out_dir)
|
|
|
|
current_cmd.append(test_file)
|
|
|
|
print('Running ' + ' '.join(current_cmd))
|
|
|
|
return_code = subprocess.call(current_cmd)
|
|
|
|
if return_code:
|
|
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
def get_test_case_code(test_case_path, flags):
|
|
|
|
# Get the test case code
|
|
|
|
code = ''
|
|
|
|
with open(test_case_path, 'r') as code_file:
|
|
|
|
code = code_file.read()
|
|
|
|
|
|
|
|
code += RUN_CLANG_DOC.format(flags)
|
|
|
|
return code
|
|
|
|
|
|
|
|
|
2018-08-17 05:54:34 +08:00
|
|
|
def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer,
|
|
|
|
check_next=True):
|
2018-07-21 07:00:34 +08:00
|
|
|
output = ''
|
|
|
|
run_cmd = ''
|
|
|
|
if '--dump-mapper' in flags or '--dump-intermediate' in flags:
|
|
|
|
# Run llvm-bcanalyzer
|
|
|
|
output = subprocess.check_output(
|
|
|
|
[bcanalyzer, '--dump',
|
|
|
|
os.path.join(root, out_file)])
|
|
|
|
output = output[:output.find('Summary of ')].rstrip()
|
|
|
|
run_cmd = RUN.format('llvm-bcanalyzer --dump',
|
|
|
|
os.path.join('docs', 'bc', out_file), checkname)
|
|
|
|
else:
|
|
|
|
# Run cat
|
|
|
|
output = subprocess.check_output(['cat', os.path.join(root, out_file)])
|
|
|
|
run_cmd = RUN.format(
|
|
|
|
'cat',
|
|
|
|
os.path.join('docs', os.path.relpath(root, case_out_path),
|
|
|
|
out_file), checkname)
|
|
|
|
|
|
|
|
# Format output.
|
|
|
|
output = output.replace('blob data = \'test\'', 'blob data = \'{{.*}}\'')
|
2018-08-03 04:10:17 +08:00
|
|
|
output = re.sub(YAML_USR_REGEX, YAML_USR, output)
|
|
|
|
output = re.sub(BITCODE_USR_REGEX, BITCODE_USR, output)
|
2018-07-21 07:00:34 +08:00
|
|
|
output = CHECK.format(checkname) + output.rstrip()
|
2018-08-17 05:54:34 +08:00
|
|
|
|
|
|
|
if check_next:
|
|
|
|
check_comment = CHECK_NEXT.format(checkname)
|
|
|
|
else:
|
|
|
|
check_comment = CHECK.format(checkname)
|
|
|
|
|
|
|
|
output = output.replace('\n', '\n' + check_comment)
|
|
|
|
output = run_cmd + output.replace('%s\n' % check_comment, "")
|
2018-07-21 07:00:34 +08:00
|
|
|
|
|
|
|
return output + '\n'
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
parser = argparse.ArgumentParser(description='Generate clang-doc tests.')
|
|
|
|
parser.add_argument(
|
|
|
|
'-flag',
|
|
|
|
action='append',
|
|
|
|
default=[],
|
|
|
|
dest='flags',
|
|
|
|
help='Flags to pass to clang-doc.')
|
|
|
|
parser.add_argument(
|
|
|
|
'-prefix',
|
|
|
|
type=str,
|
|
|
|
default='',
|
|
|
|
dest='prefix',
|
|
|
|
help='Prefix for this test group.')
|
|
|
|
parser.add_argument(
|
|
|
|
'-clang-doc-binary',
|
|
|
|
dest='clangdoc',
|
|
|
|
metavar="PATH",
|
|
|
|
default='clang-doc',
|
|
|
|
help='path to clang-doc binary')
|
|
|
|
parser.add_argument(
|
|
|
|
'-llvm-bcanalyzer-binary',
|
|
|
|
dest='bcanalyzer',
|
|
|
|
metavar="PATH",
|
|
|
|
default='llvm-bcanalyzer',
|
|
|
|
help='path to llvm-bcanalyzer binary')
|
2018-08-17 05:54:34 +08:00
|
|
|
parser.add_argument(
|
|
|
|
'-use-check-next',
|
|
|
|
dest='check_next',
|
|
|
|
default=False,
|
|
|
|
action='store_true',
|
|
|
|
help='Whether or not to use CHECK-NEXT in the resulting tests.')
|
2018-07-21 07:00:34 +08:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
flags = ' '.join(args.flags)
|
|
|
|
|
|
|
|
clang_doc_path = os.path.dirname(__file__)
|
|
|
|
tests_path = os.path.join(clang_doc_path, '..', 'test', 'clang-doc')
|
|
|
|
test_cases_path = os.path.join(tests_path, 'test_cases')
|
|
|
|
|
|
|
|
clear_test_prefix_files(args.prefix, tests_path)
|
|
|
|
|
|
|
|
for test_case_path in glob.glob(os.path.join(test_cases_path, '*')):
|
|
|
|
if test_case_path.endswith(
|
|
|
|
'compile_flags.txt') or test_case_path.endswith(
|
|
|
|
'compile_commands.json'):
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Name of this test case
|
|
|
|
case_name = os.path.basename(test_case_path).split('.')[0]
|
|
|
|
|
|
|
|
test_file = copy_to_test_file(test_case_path, test_cases_path)
|
|
|
|
out_dir = os.path.join(test_cases_path, case_name)
|
|
|
|
|
|
|
|
if run_clang_doc(args, out_dir, test_file):
|
|
|
|
return 1
|
|
|
|
|
|
|
|
# Retrieve output and format as FileCheck tests
|
|
|
|
all_output = ''
|
|
|
|
num_outputs = 0
|
|
|
|
for root, dirs, files in os.walk(out_dir):
|
|
|
|
for out_file in files:
|
|
|
|
# Make the file check the first 3 letters (there's a very small chance
|
|
|
|
# that this will collide, but the fix is to simply change the decl name)
|
|
|
|
usr = os.path.basename(out_file).split('.')
|
|
|
|
# If the usr is less than 2, this isn't one of the test files.
|
|
|
|
if len(usr) < 2:
|
|
|
|
continue
|
|
|
|
all_output += get_output(root, out_file, out_dir, args.flags,
|
2018-08-17 05:54:34 +08:00
|
|
|
num_outputs, args.bcanalyzer,
|
|
|
|
args.check_next)
|
2018-07-21 07:00:34 +08:00
|
|
|
num_outputs += 1
|
|
|
|
|
|
|
|
# Add test case code to test
|
|
|
|
all_output = get_test_case_code(test_case_path,
|
|
|
|
flags) + '\n' + all_output
|
|
|
|
|
|
|
|
# Write to test case file in /test.
|
|
|
|
test_out_path = os.path.join(
|
|
|
|
tests_path, args.prefix + '-' + os.path.basename(test_case_path))
|
|
|
|
with open(test_out_path, 'w+') as o:
|
|
|
|
o.write(all_output)
|
|
|
|
|
|
|
|
# Clean up
|
|
|
|
shutil.rmtree(out_dir)
|
|
|
|
os.remove(test_file)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|