blob: b1d1867312405bc09099b3c52369627a84b08d3e [file] [log] [blame]
Martin Hořeňovský81159832017-01-20 12:28:40 +01001#!/usr/bin/env python
2
Phil Nash1f71d1f2017-02-15 11:54:47 +00003from __future__ import print_function
Martin Moene77c9edf2014-03-08 11:31:38 +01004
Phil Nash7673a302012-11-15 22:15:41 +00005import os
6import sys
7import re
8import datetime
Phil Nashc4a089c2013-12-03 18:52:41 +00009import string
Martin Hořeňovský93f84b52017-07-09 20:58:51 +020010from glob import glob
Phil Nash7673a302012-11-15 22:15:41 +000011
Phil Nash22784512013-04-24 18:58:57 +010012from scriptCommon import catchPath
13
Martin Hořeňovskýdee61df2017-08-24 21:59:06 +020014def generate(v):
15 includesParser = re.compile( r'\s*#\s*include\s*"(.*)"' )
16 guardParser = re.compile( r'\s*#.*(TWOBLUECUBES_)?CATCH_.*_INCLUDED')
17 defineParser = re.compile( r'\s*#define\s+(TWOBLUECUBES_)?CATCH_.*_INCLUDED')
18 ifParser = re.compile( r'\s*#ifndef (TWOBLUECUBES_)?CATCH_.*_INCLUDED')
19 endIfParser = re.compile( r'\s*#endif // (TWOBLUECUBES_)?CATCH_.*_INCLUDED')
20 ifImplParser = re.compile( r'\s*#ifdef CATCH_CONFIG_RUNNER' )
21 commentParser1 = re.compile( r'^\s*/\*')
22 commentParser2 = re.compile( r'^ \*')
23 blankParser = re.compile( r'^\s*$')
Phil Nash21f7ef62015-06-29 18:05:23 +010024
Martin Hořeňovskýdee61df2017-08-24 21:59:06 +020025 seenHeaders = set([])
26 rootPath = os.path.join( catchPath, 'include/' )
27 outputPath = os.path.join( catchPath, 'single_include/catch.hpp' )
Martin Hořeňovský93f84b52017-07-09 20:58:51 +020028
Martin Hořeňovskýdee61df2017-08-24 21:59:06 +020029 globals = {
30 'includeImpl' : True,
31 'ifdefs' : 0,
32 'implIfDefs' : -1
33 }
Phil Nash7673a302012-11-15 22:15:41 +000034
Martin Hořeňovskýdee61df2017-08-24 21:59:06 +020035 for arg in sys.argv[1:]:
36 arg = string.lower(arg)
37 if arg == "noimpl":
38 globals['includeImpl'] = False
39 print( "Not including impl code" )
Phil Nashde49ec42013-12-04 20:25:14 +000040 else:
Martin Hořeňovskýdee61df2017-08-24 21:59:06 +020041 print( "\n** Unrecognised argument: " + arg + " **\n" )
42 exit(1)
Phil Nash7673a302012-11-15 22:15:41 +000043
Phil Nash7673a302012-11-15 22:15:41 +000044
Martin Hořeňovskýdee61df2017-08-24 21:59:06 +020045 # ensure that the output directory exists (hopefully no races)
46 outDir = os.path.dirname(outputPath)
47 if not os.path.exists(outDir):
48 os.makedirs(outDir)
49 out = open( outputPath, 'w' )
Phil Nash7673a302012-11-15 22:15:41 +000050
Martin Hořeňovskýdee61df2017-08-24 21:59:06 +020051 def write( line ):
52 if globals['includeImpl'] or globals['implIfDefs'] == -1:
53 out.write( line )
Phil Nash7673a302012-11-15 22:15:41 +000054
Martin Hořeňovskýdee61df2017-08-24 21:59:06 +020055 def insertCpps():
56 dirs = [os.path.join( rootPath, s) for s in ['', 'internal', 'reporters']]
57 cppFiles = []
58 for dir in dirs:
59 cppFiles += glob(os.path.join(dir, '*.cpp'))
Martin Hořeňovský46e28792017-08-30 15:42:23 +020060 # To minimize random diffs, sort the files before processing them
61 for fname in sorted(cppFiles):
Martin Hořeňovskýdee61df2017-08-24 21:59:06 +020062 dir, name = fname.rsplit(os.path.sep, 1)
63 dir += os.path.sep
64 parseFile(dir, name)
Phil Nash0c90ab32012-11-16 20:43:27 +000065
Martin Hořeňovskýdee61df2017-08-24 21:59:06 +020066 def parseFile( path, filename ):
67 f = open( os.path.join(path, filename), 'r' )
68 blanks = 0
69 write( "// start {0}\n".format( filename ) )
70 for line in f:
71 if '// ~*~* CATCH_CPP_STITCH_PLACE *~*~' in line:
72 insertCpps()
73 continue
74 elif ifParser.match( line ):
75 globals['ifdefs'] += 1
76 elif endIfParser.match( line ):
77 globals['ifdefs'] -= 1
78 if globals['ifdefs'] == globals['implIfDefs']:
79 globals['implIfDefs'] = -1
80 m = includesParser.match( line )
81 if m:
82 header = m.group(1)
83 headerPath, sep, headerFile = header.rpartition( "/" )
84 if not headerFile in seenHeaders:
85 if headerFile != "tbc_text_format.h" and headerFile != "clara.h":
86 seenHeaders.add( headerFile )
87 if headerPath == "internal" and path.endswith("internal/"):
88 headerPath = ""
89 sep = ""
90 if os.path.exists( path + headerPath + sep + headerFile ):
91 parseFile( path + headerPath + sep, headerFile )
92 else:
93 parseFile( rootPath + headerPath + sep, headerFile )
94 else:
95 if ifImplParser.match(line):
96 globals['implIfDefs'] = globals['ifdefs']
97 if (not guardParser.match( line ) or defineParser.match( line ) ) and not commentParser1.match( line )and not commentParser2.match( line ):
98 if blankParser.match( line ):
99 blanks = blanks + 1
100 else:
101 blanks = 0
102 if blanks < 2 and not defineParser.match(line):
103 write( line.rstrip() + "\n" )
104 write( '// end {}\n'.format(filename) )
105
106
107 out.write( "/*\n" )
108 out.write( " * Catch v{0}\n".format( v.getVersionString() ) )
109 out.write( " * Generated: {0}\n".format( datetime.datetime.now() ) )
110 out.write( " * ----------------------------------------------------------\n" )
111 out.write( " * This file has been merged from multiple headers. Please don't edit it directly\n" )
112 out.write( " * Copyright (c) {} Two Blue Cubes Ltd. All rights reserved.\n".format( datetime.date.today().year ) )
113 out.write( " *\n" )
114 out.write( " * Distributed under the Boost Software License, Version 1.0. (See accompanying\n" )
115 out.write( " * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n" )
116 out.write( " */\n" )
117 out.write( "#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n" )
118 out.write( "#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n" )
119
120 parseFile( rootPath, 'catch.hpp' )
121
122 out.write( "#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n\n" )
123 out.close()
124 print ("Generated single include for Catch v{0}\n".format( v.getVersionString() ) )
125
126
127if __name__ == '__main__':
128 from releaseCommon import Version
129 generate(Version())