hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 1 | #!/usr/bin/python2.7 |
| 2 | |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 3 | import os |
| 4 | import sys |
onqtam | 778e94b | 2017-05-10 23:19:50 +0300 | [diff] [blame] | 5 | import pprint |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 6 | import argparse |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 7 | import urllib |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 8 | from datetime import datetime |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 9 | import shutil |
| 10 | from time import sleep |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 11 | |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 12 | # ============================================================================== |
| 13 | # == ARGUMENTS ================================================================= |
| 14 | # ============================================================================== |
onqtam | c6555cf | 2016-09-20 15:07:28 +0300 | [diff] [blame] | 15 | |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 16 | def addCommonFlags(parser): |
onqtam | 7d8ccec | 2017-05-11 19:31:15 +0300 | [diff] [blame] | 17 | parser.add_argument("compiler", choices=['msvc', 'gcc', 'clang'], default='msvc', help = "compiler to use") |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 18 | parser.add_argument("--debug", action = "store_true", help = "build in debug") |
onqtam | dc92a3c | 2017-05-11 00:08:50 +0300 | [diff] [blame] | 19 | parser.add_argument("--catch", action = "store_true", help = "use Catch instead of doctest") |
| 20 | parser.add_argument("--disabled", action = "store_true", help = "<doctest> define DOCTEST_CONFIG_DISABLE") |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 21 | parser.add_argument("--fast", action = "store_true", help = "define the doctest/Catch fast config identifier") |
onqtam | 778e94b | 2017-05-10 23:19:50 +0300 | [diff] [blame] | 22 | parser.add_argument("--files", type=int, default=1, help = "number of source files (besides the implementation)") |
| 23 | parser.add_argument("--tests", type=int, default=1, help = "number of test cases per source file") |
| 24 | parser.add_argument("--checks", type=int, default=1, help = "number of asserts per test case") |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 25 | parser.add_argument("--asserts", choices=['normal', 'binary', 'fast'], default="normal", |
| 26 | help = "<doctest> type of assert used - Catch: only normal") |
onqtam | 956d4ec | 2016-09-18 13:42:33 +0300 | [diff] [blame] | 27 | |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 28 | parser = argparse.ArgumentParser() |
| 29 | subparsers = parser.add_subparsers() |
| 30 | parser_c = subparsers.add_parser('compile', help='benchmark compile times') |
| 31 | addCommonFlags(parser_c) |
| 32 | parser_c.add_argument("--implement", action = "store_true", help = "implement the framework test runner") |
| 33 | parser_c.add_argument("--header", action = "store_true", help = "include the framework header everywhere") |
| 34 | parser_r = subparsers.add_parser('runtime', help='benchmark runtime') |
| 35 | addCommonFlags(parser_r) |
| 36 | parser_r.add_argument("--loop-iters", type=int, default=1000, help = "loop N times all asserts in each test case") |
onqtam | 7d8ccec | 2017-05-11 19:31:15 +0300 | [diff] [blame] | 37 | parser_r.add_argument("--info", action = "store_true", help = "log the loop variable with INFO()") |
onqtam | df09f51 | 2016-09-15 13:43:50 +0300 | [diff] [blame] | 38 | |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 39 | def compile(args): args.compile = True; args.runtime = False |
| 40 | def runtime(args): args.compile = False; args.runtime = True |
| 41 | parser_c.set_defaults(func=compile) |
| 42 | parser_r.set_defaults(func=runtime) |
| 43 | args = parser.parse_args() |
| 44 | args.func(args) |
| 45 | |
onqtam | 778e94b | 2017-05-10 23:19:50 +0300 | [diff] [blame] | 46 | print("== PASSED OPTIONS TO BENCHMARK SCRIPT:") |
| 47 | pprint.pprint(vars(args), width = 1) |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 48 | |
| 49 | # ============================================================================== |
| 50 | # == SETUP ENVIRONMENT ========================================================= |
| 51 | # ============================================================================== |
| 52 | |
| 53 | # catch version |
| 54 | catch_ver = "1.9.3" |
| 55 | catch_header = "catch." + catch_ver + ".hpp" |
| 56 | |
| 57 | # get the catch header |
| 58 | if not os.path.exists("catch." + catch_ver + ".hpp"): |
| 59 | urllib.urlretrieve ("https://raw.githubusercontent.com/philsquared/Catch/v" + catch_ver + "/single_include/catch.hpp", catch_header) |
| 60 | |
| 61 | # folder with generated code |
| 62 | the_folder = 'project' |
| 63 | |
| 64 | # delete the folder |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 65 | if os.path.exists(the_folder): |
| 66 | shutil.rmtree(the_folder) |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 67 | |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 68 | # wait a bit or the script might fail... |
| 69 | sleep(2) |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 70 | |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 71 | # create the folder |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 72 | if not os.path.exists(the_folder): |
| 73 | os.makedirs(the_folder) |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 74 | |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 75 | # enter folder |
| 76 | os.chdir(the_folder); |
| 77 | |
| 78 | # ============================================================================== |
| 79 | # == DO STUFF ================================================================== |
| 80 | # ============================================================================== |
| 81 | |
| 82 | # setup defines used |
| 83 | defines = "" |
onqtam | dc92a3c | 2017-05-11 00:08:50 +0300 | [diff] [blame] | 84 | if args.disabled: |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 85 | defines += "#define DOCTEST_CONFIG_DISABLE\n" |
| 86 | if args.catch and args.fast: |
| 87 | defines += "#define CATCH_CONFIG_FAST_COMPILE\n" |
| 88 | if not args.catch and args.fast: |
| 89 | defines += "#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS\n" |
| 90 | |
| 91 | define_implement = "#define DOCTEST_CONFIG_IMPLEMENT\n" |
| 92 | if args.catch: |
| 93 | define_implement = "#define CATCH_CONFIG_RUNNER\n" |
| 94 | |
| 95 | # setup the macros used |
| 96 | macro = " CHECK(a == b);\n" |
onqtam | 7f2c0ba | 2017-05-12 03:10:01 +0300 | [diff] [blame] | 97 | if args.runtime: |
| 98 | macro = " CHECK(i == i);\n" |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 99 | if not args.catch and args.asserts == "binary": |
| 100 | macro = " CHECK_EQ(a, b);\n" |
| 101 | if not args.catch and args.asserts == "fast": |
| 102 | macro = " FAST_CHECK_EQ(a, b);\n" |
| 103 | |
| 104 | # setup the header used |
| 105 | include = '#include "doctest.h"\n' |
| 106 | if args.catch: |
| 107 | include = '#include "' + catch_header + '"\n' |
| 108 | |
| 109 | # ============================================================================== |
| 110 | # == GENERATE SOURCE CODE ====================================================== |
| 111 | # ============================================================================== |
| 112 | |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 113 | # make the source files |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 114 | for i in range(0, args.files): |
| 115 | f = open(str(i) + '.cpp', 'w') |
| 116 | if args.runtime or args.header: |
| 117 | f.write(defines) |
| 118 | f.write(include) |
| 119 | for t in range(0, args.tests): |
onqtam | 200e888 | 2016-09-20 11:01:02 +0300 | [diff] [blame] | 120 | f.write('TEST_CASE("") {\n') |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 121 | f.write(' int a = 5;\n') |
| 122 | f.write(' int b = 5;\n') |
| 123 | if args.runtime and args.loop_iters > 0: |
| 124 | f.write(' for(int i = 0; i < ' + str(args.loop_iters) + '; ++i) {\n') |
onqtam | 778e94b | 2017-05-10 23:19:50 +0300 | [diff] [blame] | 125 | if args.runtime and args.info: |
| 126 | f.write(' INFO(i);\n') |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 127 | for a in range(0, args.checks): |
| 128 | if args.runtime and args.loop_iters > 0: |
| 129 | f.write(' ') |
| 130 | f.write(macro) |
| 131 | if args.runtime and args.loop_iters > 0: |
| 132 | f.write(' }\n') |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 133 | f.write('}\n\n') |
| 134 | f.write('int f' + str(i) + '() { return ' + str(i) + '; }\n\n') |
| 135 | f.close() |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 136 | |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 137 | # the main file |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 138 | f = open('main.cpp', 'w') |
onqtam | dc92a3c | 2017-05-11 00:08:50 +0300 | [diff] [blame] | 139 | if args.runtime or args.implement or args.header: |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 140 | f.write(defines) |
| 141 | f.write(define_implement) |
| 142 | f.write(include) |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 143 | f.write('int main(int argc, char** argv) {\n') |
onqtam | dc92a3c | 2017-05-11 00:08:50 +0300 | [diff] [blame] | 144 | if args.runtime or args.implement or args.header: |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 145 | if not args.catch: f.write(' int res = doctest::Context(argc, argv).run();\n') |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 146 | else: f.write(' int res = Catch::Session().run(argc, argv);\n') |
| 147 | else: |
| 148 | f.write(' int res = 0;\n') |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 149 | for i in range(0, args.files): |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 150 | f.write(' int f' + str(i) + '(); res += f' + str(i) + '();\n') |
| 151 | f.write(' return res;\n}\n') |
| 152 | f.close() |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 153 | |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 154 | # the cmake file |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 155 | f = open('CMakeLists.txt', 'w') |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 156 | f.write('cmake_minimum_required(VERSION 2.8)\n\n') |
onqtam | 9497821 | 2016-05-22 00:07:30 +0300 | [diff] [blame] | 157 | f.write('project(bench)\n\n') |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 158 | if not args.catch: f.write('include_directories("../../../doctest/")\n\n') |
| 159 | else: f.write('include_directories("../")\n\n') |
onqtam | 9497821 | 2016-05-22 00:07:30 +0300 | [diff] [blame] | 160 | f.write('add_executable(bench main.cpp\n') |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 161 | for i in range(0, args.files): |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 162 | f.write(' ' + str(i) + '.cpp\n') |
| 163 | f.write(')\n') |
| 164 | f.close() |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 165 | |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 166 | # ============================================================================== |
| 167 | # == INVOKE CMAKE ============================================================== |
| 168 | # ============================================================================== |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 169 | |
onqtam | dc92a3c | 2017-05-11 00:08:50 +0300 | [diff] [blame] | 170 | compiler = "" |
onqtam | 7d8ccec | 2017-05-11 19:31:15 +0300 | [diff] [blame] | 171 | if args.compiler == 'clang': |
onqtam | 7f2c0ba | 2017-05-12 03:10:01 +0300 | [diff] [blame] | 172 | compiler = " -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS=-w" |
onqtam | 7d8ccec | 2017-05-11 19:31:15 +0300 | [diff] [blame] | 173 | if args.compiler == 'gcc': |
onqtam | 7f2c0ba | 2017-05-12 03:10:01 +0300 | [diff] [blame] | 174 | compiler = " -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS=-w" |
onqtam | dc92a3c | 2017-05-11 00:08:50 +0300 | [diff] [blame] | 175 | |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 176 | # setup cmake command |
| 177 | cmake_command = 'cmake . -G "Visual Studio 15 Win64"' # MSVC 2017 |
onqtam | 7d8ccec | 2017-05-11 19:31:15 +0300 | [diff] [blame] | 178 | if args.compiler != 'msvc': |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 179 | cmake_command = 'cmake . -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=' + ('Debug' if args.debug else 'Release') |
| 180 | if os.name != "nt": |
| 181 | cmake_command = 'cmake . -DCMAKE_BUILD_TYPE=' + ('Debug' if args.debug else 'Release') |
| 182 | |
onqtam | dc92a3c | 2017-05-11 00:08:50 +0300 | [diff] [blame] | 183 | os.system(cmake_command + compiler) |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 184 | |
| 185 | # ============================================================================== |
| 186 | # == BUILD PROJECT ============================================================= |
| 187 | # ============================================================================== |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 188 | |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 189 | the_config = '' |
onqtam | 7d8ccec | 2017-05-11 19:31:15 +0300 | [diff] [blame] | 190 | if args.compiler == 'msvc': |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 191 | if args.debug: the_config = ' --config Debug' |
| 192 | else: the_config = ' --config Release' |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 193 | |
onqtam | d4669c1 | 2016-05-22 00:24:44 +0300 | [diff] [blame] | 194 | # build it |
| 195 | start = datetime.now() |
onqtam | 5456545 | 2016-05-19 18:09:20 +0300 | [diff] [blame] | 196 | os.system('cmake --build .' + the_config) |
| 197 | end = datetime.now() |
onqtam | d4669c1 | 2016-05-22 00:24:44 +0300 | [diff] [blame] | 198 | |
onqtam | 7f2c0ba | 2017-05-12 03:10:01 +0300 | [diff] [blame] | 199 | if not args.runtime: |
| 200 | print("Time running compiler (+ linker) in seconds: " + str((end - start).total_seconds())) |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 201 | |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 202 | # ============================================================================== |
| 203 | # == RUN PROJECT =============================================================== |
| 204 | # ============================================================================== |
| 205 | |
| 206 | if args.runtime: |
| 207 | start = datetime.now() |
onqtam | 7d8ccec | 2017-05-11 19:31:15 +0300 | [diff] [blame] | 208 | if args.compiler == 'msvc': |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 209 | os.system(('Debug' if args.debug else 'Release') + '\\bench.exe') |
| 210 | elif os.name == "nt": |
| 211 | os.system('bench.exe') |
| 212 | else: |
| 213 | os.system('./bench') |
| 214 | end = datetime.now() |
| 215 | |
onqtam | 706eb4a | 2017-05-11 12:43:23 +0300 | [diff] [blame] | 216 | print("Time running the tests in seconds: " + str((end - start).total_seconds())) |
onqtam | fea36c5 | 2017-05-10 22:52:57 +0300 | [diff] [blame] | 217 | |
| 218 | # leave folder |
hardly | b1e7e14 | 2014-08-06 00:43:51 +0300 | [diff] [blame] | 219 | os.chdir("../"); |
| 220 | |
| 221 | |
| 222 | |
| 223 | |
| 224 | |
| 225 | |
| 226 | |
| 227 | |
| 228 | |
| 229 | |
| 230 | |
| 231 | |
| 232 | |
| 233 | |