| #!/usr/bin/python2.7 |
| |
| import os |
| import sys |
| import pprint |
| import argparse |
| import urllib |
| from datetime import datetime |
| import shutil |
| from time import sleep |
| |
| # ============================================================================== |
| # == ARGUMENTS ================================================================= |
| # ============================================================================== |
| |
| def addCommonFlags(parser): |
| parser.add_argument("compiler", choices=['msvc', 'gcc', 'clang'], default='msvc', help = "compiler to use") |
| parser.add_argument("--debug", action = "store_true", help = "build in debug") |
| parser.add_argument("--catch", action = "store_true", help = "use Catch instead of doctest") |
| parser.add_argument("--disabled", action = "store_true", help = "<doctest> define DOCTEST_CONFIG_DISABLE") |
| parser.add_argument("--fast", action = "store_true", help = "define the doctest/Catch fast config identifier") |
| parser.add_argument("--files", type=int, default=1, help = "number of source files (besides the implementation)") |
| parser.add_argument("--tests", type=int, default=1, help = "number of test cases per source file") |
| parser.add_argument("--checks", type=int, default=1, help = "number of asserts per test case") |
| parser.add_argument("--asserts", choices=['normal', 'binary', 'fast'], default="normal", |
| help = "<doctest> type of assert used - Catch: only normal") |
| |
| parser = argparse.ArgumentParser() |
| subparsers = parser.add_subparsers() |
| parser_c = subparsers.add_parser('compile', help='benchmark compile times') |
| addCommonFlags(parser_c) |
| parser_c.add_argument("--implement", action = "store_true", help = "implement the framework test runner") |
| parser_c.add_argument("--header", action = "store_true", help = "include the framework header everywhere") |
| parser_r = subparsers.add_parser('runtime', help='benchmark runtime') |
| addCommonFlags(parser_r) |
| parser_r.add_argument("--loop-iters", type=int, default=1000, help = "loop N times all asserts in each test case") |
| parser_r.add_argument("--info", action = "store_true", help = "log the loop variable with INFO()") |
| |
| def compile(args): args.compile = True; args.runtime = False |
| def runtime(args): args.compile = False; args.runtime = True |
| parser_c.set_defaults(func=compile) |
| parser_r.set_defaults(func=runtime) |
| args = parser.parse_args() |
| args.func(args) |
| |
| print("== PASSED OPTIONS TO BENCHMARK SCRIPT:") |
| pprint.pprint(vars(args), width = 1) |
| |
| # ============================================================================== |
| # == SETUP ENVIRONMENT ========================================================= |
| # ============================================================================== |
| |
| # catch version |
| catch_ver = "1.9.3" |
| catch_header = "catch." + catch_ver + ".hpp" |
| |
| # get the catch header |
| if not os.path.exists("catch." + catch_ver + ".hpp"): |
| urllib.urlretrieve ("https://raw.githubusercontent.com/philsquared/Catch/v" + catch_ver + "/single_include/catch.hpp", catch_header) |
| |
| # folder with generated code |
| the_folder = 'project' |
| |
| # delete the folder |
| if os.path.exists(the_folder): |
| shutil.rmtree(the_folder) |
| |
| # wait a bit or the script might fail... |
| sleep(2) |
| |
| # create the folder |
| if not os.path.exists(the_folder): |
| os.makedirs(the_folder) |
| |
| # enter folder |
| os.chdir(the_folder); |
| |
| # ============================================================================== |
| # == DO STUFF ================================================================== |
| # ============================================================================== |
| |
| # setup defines used |
| defines = "" |
| if args.disabled: |
| defines += "#define DOCTEST_CONFIG_DISABLE\n" |
| if args.catch and args.fast: |
| defines += "#define CATCH_CONFIG_FAST_COMPILE\n" |
| if not args.catch and args.fast: |
| defines += "#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS\n" |
| |
| define_implement = "#define DOCTEST_CONFIG_IMPLEMENT\n" |
| if args.catch: |
| define_implement = "#define CATCH_CONFIG_RUNNER\n" |
| |
| # setup the macros used |
| macro = " CHECK(a == b);\n" |
| if args.runtime: |
| macro = " CHECK(i == i);\n" |
| if not args.catch and args.asserts == "binary": |
| macro = " CHECK_EQ(a, b);\n" |
| if not args.catch and args.asserts == "fast": |
| macro = " FAST_CHECK_EQ(a, b);\n" |
| |
| # setup the header used |
| include = '#include "doctest.h"\n' |
| if args.catch: |
| include = '#include "' + catch_header + '"\n' |
| |
| # ============================================================================== |
| # == GENERATE SOURCE CODE ====================================================== |
| # ============================================================================== |
| |
| # make the source files |
| for i in range(0, args.files): |
| f = open(str(i) + '.cpp', 'w') |
| if args.runtime or args.header: |
| f.write(defines) |
| f.write(include) |
| for t in range(0, args.tests): |
| f.write('TEST_CASE("") {\n') |
| f.write(' int a = 5;\n') |
| f.write(' int b = 5;\n') |
| if args.runtime and args.loop_iters > 0: |
| f.write(' for(int i = 0; i < ' + str(args.loop_iters) + '; ++i) {\n') |
| if args.runtime and args.info: |
| f.write(' INFO(i);\n') |
| for a in range(0, args.checks): |
| if args.runtime and args.loop_iters > 0: |
| f.write(' ') |
| f.write(macro) |
| if args.runtime and args.loop_iters > 0: |
| f.write(' }\n') |
| f.write('}\n\n') |
| f.write('int f' + str(i) + '() { return ' + str(i) + '; }\n\n') |
| f.close() |
| |
| # the main file |
| f = open('main.cpp', 'w') |
| if args.runtime or args.implement or args.header: |
| f.write(defines) |
| f.write(define_implement) |
| f.write(include) |
| f.write('int main(int argc, char** argv) {\n') |
| if args.runtime or args.implement or args.header: |
| if not args.catch: f.write(' int res = doctest::Context(argc, argv).run();\n') |
| else: f.write(' int res = Catch::Session().run(argc, argv);\n') |
| else: |
| f.write(' int res = 0;\n') |
| for i in range(0, args.files): |
| f.write(' int f' + str(i) + '(); res += f' + str(i) + '();\n') |
| f.write(' return res;\n}\n') |
| f.close() |
| |
| # the cmake file |
| f = open('CMakeLists.txt', 'w') |
| f.write('cmake_minimum_required(VERSION 2.8)\n\n') |
| f.write('project(bench)\n\n') |
| if not args.catch: f.write('include_directories("../../../doctest/")\n\n') |
| else: f.write('include_directories("../")\n\n') |
| f.write('add_executable(bench main.cpp\n') |
| for i in range(0, args.files): |
| f.write(' ' + str(i) + '.cpp\n') |
| f.write(')\n') |
| f.close() |
| |
| # ============================================================================== |
| # == INVOKE CMAKE ============================================================== |
| # ============================================================================== |
| |
| compiler = "" |
| if args.compiler == 'clang': |
| compiler = " -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS=-w" |
| if args.compiler == 'gcc': |
| compiler = " -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS=-w" |
| |
| # setup cmake command |
| cmake_command = 'cmake . -G "Visual Studio 15 Win64"' # MSVC 2017 |
| if args.compiler != 'msvc': |
| cmake_command = 'cmake . -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=' + ('Debug' if args.debug else 'Release') |
| if os.name != "nt": |
| cmake_command = 'cmake . -DCMAKE_BUILD_TYPE=' + ('Debug' if args.debug else 'Release') |
| |
| os.system(cmake_command + compiler) |
| |
| # ============================================================================== |
| # == BUILD PROJECT ============================================================= |
| # ============================================================================== |
| |
| the_config = '' |
| if args.compiler == 'msvc': |
| if args.debug: the_config = ' --config Debug' |
| else: the_config = ' --config Release' |
| |
| # build it |
| start = datetime.now() |
| os.system('cmake --build .' + the_config) |
| end = datetime.now() |
| |
| if not args.runtime: |
| print("Time running compiler (+ linker) in seconds: " + str((end - start).total_seconds())) |
| |
| # ============================================================================== |
| # == RUN PROJECT =============================================================== |
| # ============================================================================== |
| |
| if args.runtime: |
| start = datetime.now() |
| if args.compiler == 'msvc': |
| os.system(('Debug' if args.debug else 'Release') + '\\bench.exe') |
| elif os.name == "nt": |
| os.system('bench.exe') |
| else: |
| os.system('./bench') |
| end = datetime.now() |
| |
| print("Time running the tests in seconds: " + str((end - start).total_seconds())) |
| |
| # leave folder |
| os.chdir("../"); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |