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