blob: ebb3a4c7b3612c6f8561b49ae69274d4f1c2e7ac [file] [log] [blame]
onqtam24bd9622018-08-21 10:10:23 +03001#!/usr/bin/python3
hardlyb1e7e142014-08-06 00:43:51 +03002
hardlyb1e7e142014-08-06 00:43:51 +03003import os
4import sys
onqtam24bd9622018-08-21 10:10:23 +03005if sys.version_info[0] < 3: raise Exception("Python 3 or a more recent version is required.")
onqtam778e94b2017-05-10 23:19:50 +03006import pprint
onqtamfea36c52017-05-10 22:52:57 +03007import argparse
onqtamfea36c52017-05-10 22:52:57 +03008import urllib
hardlyb1e7e142014-08-06 00:43:51 +03009from datetime import datetime
onqtam54565452016-05-19 18:09:20 +030010import shutil
11from time import sleep
hardlyb1e7e142014-08-06 00:43:51 +030012
onqtamfea36c52017-05-10 22:52:57 +030013# ==============================================================================
14# == ARGUMENTS =================================================================
15# ==============================================================================
onqtamc6555cf2016-09-20 15:07:28 +030016
onqtamfea36c52017-05-10 22:52:57 +030017def addCommonFlags(parser):
onqtam7d8ccec2017-05-11 19:31:15 +030018 parser.add_argument("compiler", choices=['msvc', 'gcc', 'clang'], default='msvc', help = "compiler to use")
onqtamfea36c52017-05-10 22:52:57 +030019 parser.add_argument("--debug", action = "store_true", help = "build in debug")
onqtamdc92a3c2017-05-11 00:08:50 +030020 parser.add_argument("--catch", action = "store_true", help = "use Catch instead of doctest")
onqtam87e80e52017-09-07 12:50:53 +030021 parser.add_argument("--disabled", action = "store_true", help = "DOCTEST_CONFIG_DISABLE / CATCH_CONFIG_DISABLE")
onqtamfea36c52017-05-10 22:52:57 +030022 parser.add_argument("--fast", action = "store_true", help = "define the doctest/Catch fast config identifier")
onqtam778e94b2017-05-10 23:19:50 +030023 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")
onqtam349a7f02018-12-02 16:59:54 +020026 parser.add_argument("--asserts", choices=['normal', 'binary'], default="normal",
onqtamfea36c52017-05-10 22:52:57 +030027 help = "<doctest> type of assert used - Catch: only normal")
onqtam956d4ec2016-09-18 13:42:33 +030028
onqtamfea36c52017-05-10 22:52:57 +030029parser = argparse.ArgumentParser()
30subparsers = parser.add_subparsers()
31parser_c = subparsers.add_parser('compile', help='benchmark compile times')
32addCommonFlags(parser_c)
33parser_c.add_argument("--implement", action = "store_true", help = "implement the framework test runner")
34parser_c.add_argument("--header", action = "store_true", help = "include the framework header everywhere")
35parser_r = subparsers.add_parser('runtime', help='benchmark runtime')
36addCommonFlags(parser_r)
37parser_r.add_argument("--loop-iters", type=int, default=1000, help = "loop N times all asserts in each test case")
onqtam7d8ccec2017-05-11 19:31:15 +030038parser_r.add_argument("--info", action = "store_true", help = "log the loop variable with INFO()")
onqtamdf09f512016-09-15 13:43:50 +030039
onqtamfea36c52017-05-10 22:52:57 +030040def compile(args): args.compile = True; args.runtime = False
41def runtime(args): args.compile = False; args.runtime = True
42parser_c.set_defaults(func=compile)
43parser_r.set_defaults(func=runtime)
44args = parser.parse_args()
45args.func(args)
46
onqtam778e94b2017-05-10 23:19:50 +030047print("== PASSED OPTIONS TO BENCHMARK SCRIPT:")
48pprint.pprint(vars(args), width = 1)
onqtamfea36c52017-05-10 22:52:57 +030049
50# ==============================================================================
51# == SETUP ENVIRONMENT =========================================================
52# ==============================================================================
53
54# catch version
onqtam24bd9622018-08-21 10:10:23 +030055catch_ver = "2.3.0"
onqtamfea36c52017-05-10 22:52:57 +030056catch_header = "catch." + catch_ver + ".hpp"
57
58# get the catch header
onqtam24bd9622018-08-21 10:10:23 +030059if not os.path.exists("catch." + catch_ver + ".hpp"):
60 urllib.urlretrieve("https://github.com/catchorg/Catch2/releases/download/v" + catch_ver + "/catch.hpp", catch_header)
onqtamfea36c52017-05-10 22:52:57 +030061
62# folder with generated code
63the_folder = 'project'
64
65# delete the folder
onqtam54565452016-05-19 18:09:20 +030066if os.path.exists(the_folder):
67 shutil.rmtree(the_folder)
hardlyb1e7e142014-08-06 00:43:51 +030068
onqtamfea36c52017-05-10 22:52:57 +030069# wait a bit or the script might fail...
70sleep(2)
hardlyb1e7e142014-08-06 00:43:51 +030071
onqtamfea36c52017-05-10 22:52:57 +030072# create the folder
onqtam54565452016-05-19 18:09:20 +030073if not os.path.exists(the_folder):
74 os.makedirs(the_folder)
hardlyb1e7e142014-08-06 00:43:51 +030075
onqtamfea36c52017-05-10 22:52:57 +030076# enter folder
77os.chdir(the_folder);
78
79# ==============================================================================
80# == DO STUFF ==================================================================
81# ==============================================================================
82
83# setup defines used
84defines = ""
onqtam87e80e52017-09-07 12:50:53 +030085if args.catch and args.disabled:
86 defines += "#define CATCH_CONFIG_DISABLE\n"
87if not args.catch and args.disabled:
onqtamfea36c52017-05-10 22:52:57 +030088 defines += "#define DOCTEST_CONFIG_DISABLE\n"
89if args.catch and args.fast:
90 defines += "#define CATCH_CONFIG_FAST_COMPILE\n"
91if not args.catch and args.fast:
92 defines += "#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS\n"
93
94define_implement = "#define DOCTEST_CONFIG_IMPLEMENT\n"
95if args.catch:
96 define_implement = "#define CATCH_CONFIG_RUNNER\n"
97
98# setup the macros used
99macro = " CHECK(a == b);\n"
onqtam7f2c0ba2017-05-12 03:10:01 +0300100if args.runtime:
101 macro = " CHECK(i == i);\n"
onqtamfea36c52017-05-10 22:52:57 +0300102if not args.catch and args.asserts == "binary":
103 macro = " CHECK_EQ(a, b);\n"
onqtamfea36c52017-05-10 22:52:57 +0300104
105# setup the header used
106include = '#include "doctest.h"\n'
107if args.catch:
108 include = '#include "' + catch_header + '"\n'
109
110# ==============================================================================
111# == GENERATE SOURCE CODE ======================================================
112# ==============================================================================
113
onqtam54565452016-05-19 18:09:20 +0300114# make the source files
onqtamfea36c52017-05-10 22:52:57 +0300115for 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):
onqtam200e8882016-09-20 11:01:02 +0300121 f.write('TEST_CASE("") {\n')
onqtamfea36c52017-05-10 22:52:57 +0300122 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')
onqtam778e94b2017-05-10 23:19:50 +0300126 if args.runtime and args.info:
127 f.write(' INFO(i);\n')
onqtamfea36c52017-05-10 22:52:57 +0300128 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')
onqtam54565452016-05-19 18:09:20 +0300134 f.write('}\n\n')
135 f.write('int f' + str(i) + '() { return ' + str(i) + '; }\n\n')
136 f.close()
hardlyb1e7e142014-08-06 00:43:51 +0300137
onqtam54565452016-05-19 18:09:20 +0300138# the main file
onqtamfea36c52017-05-10 22:52:57 +0300139f = open('main.cpp', 'w')
onqtamdc92a3c2017-05-11 00:08:50 +0300140if args.runtime or args.implement or args.header:
onqtamfea36c52017-05-10 22:52:57 +0300141 f.write(defines)
142 f.write(define_implement)
143 f.write(include)
onqtam54565452016-05-19 18:09:20 +0300144f.write('int main(int argc, char** argv) {\n')
onqtamdc92a3c2017-05-11 00:08:50 +0300145if args.runtime or args.implement or args.header:
onqtamfea36c52017-05-10 22:52:57 +0300146 if not args.catch: f.write(' int res = doctest::Context(argc, argv).run();\n')
onqtam54565452016-05-19 18:09:20 +0300147 else: f.write(' int res = Catch::Session().run(argc, argv);\n')
148else:
149 f.write(' int res = 0;\n')
onqtamfea36c52017-05-10 22:52:57 +0300150for i in range(0, args.files):
onqtam54565452016-05-19 18:09:20 +0300151 f.write(' int f' + str(i) + '(); res += f' + str(i) + '();\n')
152f.write(' return res;\n}\n')
153f.close()
hardlyb1e7e142014-08-06 00:43:51 +0300154
onqtam54565452016-05-19 18:09:20 +0300155# the cmake file
onqtamfea36c52017-05-10 22:52:57 +0300156f = open('CMakeLists.txt', 'w')
onqtam54565452016-05-19 18:09:20 +0300157f.write('cmake_minimum_required(VERSION 2.8)\n\n')
onqtam94978212016-05-22 00:07:30 +0300158f.write('project(bench)\n\n')
onqtam382b5b62017-09-11 12:53:51 +0300159f.write('if(NOT MSVC)\n')
160f.write('set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")\n')
161f.write('endif()\n\n')
onqtamfea36c52017-05-10 22:52:57 +0300162if not args.catch: f.write('include_directories("../../../doctest/")\n\n')
163else: f.write('include_directories("../")\n\n')
onqtam94978212016-05-22 00:07:30 +0300164f.write('add_executable(bench main.cpp\n')
onqtamfea36c52017-05-10 22:52:57 +0300165for i in range(0, args.files):
onqtam54565452016-05-19 18:09:20 +0300166 f.write(' ' + str(i) + '.cpp\n')
167f.write(')\n')
168f.close()
hardlyb1e7e142014-08-06 00:43:51 +0300169
onqtamfea36c52017-05-10 22:52:57 +0300170# ==============================================================================
171# == INVOKE CMAKE ==============================================================
172# ==============================================================================
hardlyb1e7e142014-08-06 00:43:51 +0300173
onqtamdc92a3c2017-05-11 00:08:50 +0300174compiler = ""
onqtam7d8ccec2017-05-11 19:31:15 +0300175if args.compiler == 'clang':
onqtam7f2c0ba2017-05-12 03:10:01 +0300176 compiler = " -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS=-w"
onqtam7d8ccec2017-05-11 19:31:15 +0300177if args.compiler == 'gcc':
onqtam7f2c0ba2017-05-12 03:10:01 +0300178 compiler = " -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS=-w"
onqtamdc92a3c2017-05-11 00:08:50 +0300179
onqtamfea36c52017-05-10 22:52:57 +0300180# setup cmake command
181cmake_command = 'cmake . -G "Visual Studio 15 Win64"' # MSVC 2017
onqtam7d8ccec2017-05-11 19:31:15 +0300182if args.compiler != 'msvc':
onqtamfea36c52017-05-10 22:52:57 +0300183 cmake_command = 'cmake . -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=' + ('Debug' if args.debug else 'Release')
184if os.name != "nt":
185 cmake_command = 'cmake . -DCMAKE_BUILD_TYPE=' + ('Debug' if args.debug else 'Release')
186
onqtamdc92a3c2017-05-11 00:08:50 +0300187os.system(cmake_command + compiler)
onqtamfea36c52017-05-10 22:52:57 +0300188
189# ==============================================================================
190# == BUILD PROJECT =============================================================
191# ==============================================================================
hardlyb1e7e142014-08-06 00:43:51 +0300192
onqtam54565452016-05-19 18:09:20 +0300193the_config = ''
onqtam7d8ccec2017-05-11 19:31:15 +0300194if args.compiler == 'msvc':
onqtamfea36c52017-05-10 22:52:57 +0300195 if args.debug: the_config = ' --config Debug'
196 else: the_config = ' --config Release'
hardlyb1e7e142014-08-06 00:43:51 +0300197
onqtamd4669c12016-05-22 00:24:44 +0300198# build it
199start = datetime.now()
onqtam54565452016-05-19 18:09:20 +0300200os.system('cmake --build .' + the_config)
201end = datetime.now()
onqtamd4669c12016-05-22 00:24:44 +0300202
onqtam7f2c0ba2017-05-12 03:10:01 +0300203if not args.runtime:
204 print("Time running compiler (+ linker) in seconds: " + str((end - start).total_seconds()))
hardlyb1e7e142014-08-06 00:43:51 +0300205
onqtamfea36c52017-05-10 22:52:57 +0300206# ==============================================================================
207# == RUN PROJECT ===============================================================
208# ==============================================================================
209
210if args.runtime:
211 start = datetime.now()
onqtam7d8ccec2017-05-11 19:31:15 +0300212 if args.compiler == 'msvc':
onqtamfea36c52017-05-10 22:52:57 +0300213 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
onqtam706eb4a2017-05-11 12:43:23 +0300220 print("Time running the tests in seconds: " + str((end - start).total_seconds()))
onqtamfea36c52017-05-10 22:52:57 +0300221
222# leave folder
hardlyb1e7e142014-08-06 00:43:51 +0300223os.chdir("../");
224
225
226
227
228
229
230
231
232
233
234
235
236
237