blob: fccc5da320a51bb48940c77a148a8c8417e22e57 [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")
onqtamfea36c52017-05-10 22:52:57 +030026 parser.add_argument("--asserts", choices=['normal', 'binary', 'fast'], default="normal",
27 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"
104if not args.catch and args.asserts == "fast":
105 macro = " FAST_CHECK_EQ(a, b);\n"
106
107# setup the header used
108include = '#include "doctest.h"\n'
109if args.catch:
110 include = '#include "' + catch_header + '"\n'
111
112# ==============================================================================
113# == GENERATE SOURCE CODE ======================================================
114# ==============================================================================
115
onqtam54565452016-05-19 18:09:20 +0300116# make the source files
onqtamfea36c52017-05-10 22:52:57 +0300117for i in range(0, args.files):
118 f = open(str(i) + '.cpp', 'w')
119 if args.runtime or args.header:
120 f.write(defines)
121 f.write(include)
122 for t in range(0, args.tests):
onqtam200e8882016-09-20 11:01:02 +0300123 f.write('TEST_CASE("") {\n')
onqtamfea36c52017-05-10 22:52:57 +0300124 f.write(' int a = 5;\n')
125 f.write(' int b = 5;\n')
126 if args.runtime and args.loop_iters > 0:
127 f.write(' for(int i = 0; i < ' + str(args.loop_iters) + '; ++i) {\n')
onqtam778e94b2017-05-10 23:19:50 +0300128 if args.runtime and args.info:
129 f.write(' INFO(i);\n')
onqtamfea36c52017-05-10 22:52:57 +0300130 for a in range(0, args.checks):
131 if args.runtime and args.loop_iters > 0:
132 f.write(' ')
133 f.write(macro)
134 if args.runtime and args.loop_iters > 0:
135 f.write(' }\n')
onqtam54565452016-05-19 18:09:20 +0300136 f.write('}\n\n')
137 f.write('int f' + str(i) + '() { return ' + str(i) + '; }\n\n')
138 f.close()
hardlyb1e7e142014-08-06 00:43:51 +0300139
onqtam54565452016-05-19 18:09:20 +0300140# the main file
onqtamfea36c52017-05-10 22:52:57 +0300141f = open('main.cpp', 'w')
onqtamdc92a3c2017-05-11 00:08:50 +0300142if args.runtime or args.implement or args.header:
onqtamfea36c52017-05-10 22:52:57 +0300143 f.write(defines)
144 f.write(define_implement)
145 f.write(include)
onqtam54565452016-05-19 18:09:20 +0300146f.write('int main(int argc, char** argv) {\n')
onqtamdc92a3c2017-05-11 00:08:50 +0300147if args.runtime or args.implement or args.header:
onqtamfea36c52017-05-10 22:52:57 +0300148 if not args.catch: f.write(' int res = doctest::Context(argc, argv).run();\n')
onqtam54565452016-05-19 18:09:20 +0300149 else: f.write(' int res = Catch::Session().run(argc, argv);\n')
150else:
151 f.write(' int res = 0;\n')
onqtamfea36c52017-05-10 22:52:57 +0300152for i in range(0, args.files):
onqtam54565452016-05-19 18:09:20 +0300153 f.write(' int f' + str(i) + '(); res += f' + str(i) + '();\n')
154f.write(' return res;\n}\n')
155f.close()
hardlyb1e7e142014-08-06 00:43:51 +0300156
onqtam54565452016-05-19 18:09:20 +0300157# the cmake file
onqtamfea36c52017-05-10 22:52:57 +0300158f = open('CMakeLists.txt', 'w')
onqtam54565452016-05-19 18:09:20 +0300159f.write('cmake_minimum_required(VERSION 2.8)\n\n')
onqtam94978212016-05-22 00:07:30 +0300160f.write('project(bench)\n\n')
onqtam382b5b62017-09-11 12:53:51 +0300161f.write('if(NOT MSVC)\n')
162f.write('set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")\n')
163f.write('endif()\n\n')
onqtamfea36c52017-05-10 22:52:57 +0300164if not args.catch: f.write('include_directories("../../../doctest/")\n\n')
165else: f.write('include_directories("../")\n\n')
onqtam94978212016-05-22 00:07:30 +0300166f.write('add_executable(bench main.cpp\n')
onqtamfea36c52017-05-10 22:52:57 +0300167for i in range(0, args.files):
onqtam54565452016-05-19 18:09:20 +0300168 f.write(' ' + str(i) + '.cpp\n')
169f.write(')\n')
170f.close()
hardlyb1e7e142014-08-06 00:43:51 +0300171
onqtamfea36c52017-05-10 22:52:57 +0300172# ==============================================================================
173# == INVOKE CMAKE ==============================================================
174# ==============================================================================
hardlyb1e7e142014-08-06 00:43:51 +0300175
onqtamdc92a3c2017-05-11 00:08:50 +0300176compiler = ""
onqtam7d8ccec2017-05-11 19:31:15 +0300177if args.compiler == 'clang':
onqtam7f2c0ba2017-05-12 03:10:01 +0300178 compiler = " -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS=-w"
onqtam7d8ccec2017-05-11 19:31:15 +0300179if args.compiler == 'gcc':
onqtam7f2c0ba2017-05-12 03:10:01 +0300180 compiler = " -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS=-w"
onqtamdc92a3c2017-05-11 00:08:50 +0300181
onqtamfea36c52017-05-10 22:52:57 +0300182# setup cmake command
183cmake_command = 'cmake . -G "Visual Studio 15 Win64"' # MSVC 2017
onqtam7d8ccec2017-05-11 19:31:15 +0300184if args.compiler != 'msvc':
onqtamfea36c52017-05-10 22:52:57 +0300185 cmake_command = 'cmake . -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=' + ('Debug' if args.debug else 'Release')
186if os.name != "nt":
187 cmake_command = 'cmake . -DCMAKE_BUILD_TYPE=' + ('Debug' if args.debug else 'Release')
188
onqtamdc92a3c2017-05-11 00:08:50 +0300189os.system(cmake_command + compiler)
onqtamfea36c52017-05-10 22:52:57 +0300190
191# ==============================================================================
192# == BUILD PROJECT =============================================================
193# ==============================================================================
hardlyb1e7e142014-08-06 00:43:51 +0300194
onqtam54565452016-05-19 18:09:20 +0300195the_config = ''
onqtam7d8ccec2017-05-11 19:31:15 +0300196if args.compiler == 'msvc':
onqtamfea36c52017-05-10 22:52:57 +0300197 if args.debug: the_config = ' --config Debug'
198 else: the_config = ' --config Release'
hardlyb1e7e142014-08-06 00:43:51 +0300199
onqtamd4669c12016-05-22 00:24:44 +0300200# build it
201start = datetime.now()
onqtam54565452016-05-19 18:09:20 +0300202os.system('cmake --build .' + the_config)
203end = datetime.now()
onqtamd4669c12016-05-22 00:24:44 +0300204
onqtam7f2c0ba2017-05-12 03:10:01 +0300205if not args.runtime:
206 print("Time running compiler (+ linker) in seconds: " + str((end - start).total_seconds()))
hardlyb1e7e142014-08-06 00:43:51 +0300207
onqtamfea36c52017-05-10 22:52:57 +0300208# ==============================================================================
209# == RUN PROJECT ===============================================================
210# ==============================================================================
211
212if args.runtime:
213 start = datetime.now()
onqtam7d8ccec2017-05-11 19:31:15 +0300214 if args.compiler == 'msvc':
onqtamfea36c52017-05-10 22:52:57 +0300215 os.system(('Debug' if args.debug else 'Release') + '\\bench.exe')
216 elif os.name == "nt":
217 os.system('bench.exe')
218 else:
219 os.system('./bench')
220 end = datetime.now()
221
onqtam706eb4a2017-05-11 12:43:23 +0300222 print("Time running the tests in seconds: " + str((end - start).total_seconds()))
onqtamfea36c52017-05-10 22:52:57 +0300223
224# leave folder
hardlyb1e7e142014-08-06 00:43:51 +0300225os.chdir("../");
226
227
228
229
230
231
232
233
234
235
236
237
238
239