Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/env python
      2 
      3 from __future__ import print_function
      4 
      5 import os
      6 import io
      7 import sys
      8 import re
      9 import datetime
     10 from glob import glob
     11 
     12 from scriptCommon import catchPath
     13 
     14 def 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*$')
     24 
     25     seenHeaders = set([])
     26     rootPath = os.path.join( catchPath, 'include/' )
     27     outputPath = os.path.join( catchPath, 'single_include/catch2/catch.hpp' )
     28 
     29     globals = {
     30         'includeImpl' : True,
     31         'ifdefs'      :  0,
     32         'implIfDefs'  : -1
     33     }
     34 
     35     for arg in sys.argv[1:]:
     36         arg = arg.lower()
     37         if arg == "noimpl":
     38             globals['includeImpl'] = False
     39             print( "Not including impl code" )
     40         else:
     41             print( "\n** Unrecognised argument: " + arg + " **\n" )
     42             exit(1)
     43 
     44 
     45     # 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 = io.open( outputPath, 'w', newline='\n', encoding='utf-8')
     50 
     51     def write( line ):
     52         if globals['includeImpl'] or globals['implIfDefs'] == -1:
     53             out.write( line )
     54 
     55     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'))
     60         # To minimize random diffs, sort the files before processing them
     61         for fname in sorted(cppFiles):
     62             dir, name = fname.rsplit(os.path.sep, 1)
     63             dir += os.path.sep
     64             parseFile(dir, name)
     65 
     66     def parseFile( path, filename ):
     67         f = io.open( os.path.join(path, filename), 'r', encoding='utf-8' )
     68         blanks = 0
     69         write( u"// 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 headerFile not 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( u'// end {}\n'.format(filename) )
    105 
    106 
    107     write( u"/*\n" )
    108     write( u" *  Catch v{0}\n".format( v.getVersionString() ) )
    109     write( u" *  Generated: {0}\n".format( datetime.datetime.now() ) )
    110     write( u" *  ----------------------------------------------------------\n" )
    111     write( u" *  This file has been merged from multiple headers. Please don't edit it directly\n" )
    112     write( u" *  Copyright (c) {} Two Blue Cubes Ltd. All rights reserved.\n".format( datetime.date.today().year ) )
    113     write( u" *\n" )
    114     write( u" *  Distributed under the Boost Software License, Version 1.0. (See accompanying\n" )
    115     write( u" *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n" )
    116     write( u" */\n" )
    117     write( u"#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n" )
    118     write( u"#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n" )
    119 
    120     parseFile( rootPath, 'catch.hpp' )
    121 
    122     write( u"#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 
    127 if __name__ == '__main__':
    128     from releaseCommon import Version
    129     generate(Version())
    130