Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
265 lines (220 sloc) 8.79 KB
# LICENSE
#
# This software is dual-licensed to the public domain and under the following
# license: you are granted a perpetual, irrevocable license to copy, modify,
# publish, and distribute this file as you see fit.
import os
import re
def fts_amalgamate(config, included_paths):
# Open output file
print 'Opening output file: {0}'.format(config['output'])
output_file = open(config['output'], 'w')
# Append amalgamation header
for line in config['amalgamation_header']:
output_file.write(line + '\n')
# Generate absolute paths for include filtering
exclude_paths = set()
for relpath in config['include_mask']:
exclude_paths.add(os.path.abspath(relpath))
# Append each input file
for relpath in config['input']:
fts_amalgamate_path(relpath, config, output_file, exclude_paths, included_paths)
# Save output file
print 'Saving output: {0}\n'.format(config['output'])
output_file.close()
def fts_amalgamate_path(relpath, config, output_file, exclude_paths, included_paths):
input_file = open(relpath, 'r')
input_file_abspath = os.path.abspath(relpath)
input_file_dir = os.path.dirname(input_file_abspath)
input_file_basename = os.path.basename(relpath)
# Only amalgamate a given path once
if input_file_abspath in included_paths:
return
included_paths.add(input_file_abspath)
print 'Appending input: {0}'.format(input_file_basename)
# Regex to match #includes. Used to exclude includdes for files being amalgamated
include_pattern = r'#\s*include.*([<"])(.*)[>"]'
# Get extra ops
extra_operations = None
for opsRelPath, ops in config['extra_operations'].items():
opsAbsPath = os.path.abspath(opsRelPath)
if opsAbsPath == input_file_abspath:
extra_operations = ops
break
# File Header
for line in config['file_header']:
output_file.write(line.format(relpath) + '\n')
# Extra file guard
if extra_operations and 'fileGuard' in extra_operations:
output_file.write(extra_operations['fileGuard'] + ' // (AMALGAMATION GUARD)\n\n')
# Data for commenting out code
remove_lines = None
remove_lines_cur = 0
remove_lines_len = -1
if extra_operations and 'remove_lines' in extra_operations:
remove_lines = extra_operations['remove_lines']
remove_lines_len = len(remove_lines)
# Input File
line_num = 0
for line in iter(input_file):
line_num += 1
# Comment out line if in range Begin/End block comment if line range is marked for removal
comment_out = (remove_lines_cur < remove_lines_len
and line_num >= remove_lines[remove_lines_cur]['begin']
and line_num <= remove_lines[remove_lines_cur]['end'])
# Exclude #include statements that include files being amalgamated
match = re.search(include_pattern, line)
exclude = False
if match:
include_type = match.group(1)
include_path = match.group(2)
if include_type == '\"':
include_abspath = os.path.abspath(input_file_dir + "\\" + include_path)
elif include_type == '<':
include_abspath = os.path.abspath(config['include_root'] + '\\' + include_path)
if include_abspath in exclude_paths:
exclude = True
if exclude or comment_out:
line = '// (AMALGAMATED) ' + line
if config['allow_nesting'] and exclude and not include_abspath in included_paths:
fts_amalgamate_path(include_abspath, config, output_file, exclude_paths, included_paths)
# Run any extra operations
if extra_operations and 'findReplace' in extra_operations:
for fr in extra_operations['findReplace']:
line = re.sub(fr['find'], fr['replace'], line)
# Write final line
output_file.write(line)
# Close extra file guard
if extra_operations and 'fileGuard' in extra_operations:
output_file.write('\n' + '#endif //' + extra_operations['fileGuard'] + ' (AMALGAMATION GUARD)')
# File Footer
for line in config['file_footer']:
output_file.write(line.format(relpath) + '\n')
def main():
# Header placed once at top of output file
amalgamation_header = [
'// The file was GENERATED by an amalgamation script.',
'// DO NOT EDIT BY HAND!!!\n\n\n',
]
# Header placed before the contents of each input file
file_header = [
'// ----------------------------------------------------------------------------',
'// BEGIN FILE: {0}',
'//\n\n',
]
# Footer placed after the contents of each input file
file_footer = [
'\n\n//',
'// END FILE: {0}',
'// ----------------------------------------------------------------------------\n\n\n'
]
headers = [
'zlib/zconf.h',
'zlib/zlib.h',
'zlib/zutil.h',
'zlib/gzguts.h',
'zlib/deflate.h',
'zlib/inftrees.h',
'zlib/inflate.h',
'zlib/inffast.h',
'zlib/inffixed.h',
]
include_extra_operations = {
'zlib/gzguts.h' : {
'findReplace' : [
{ 'find': 'GZIP', 'replace': 'GZIPSTREAM' },
]
},
'zlib/inflate.h' : {
'findReplace' : [
{ 'find': r'COPY([^_])', 'replace': r'COPY__\1' },
]
},
'zlib/inftrees.h' : {
'findReplace' : [
{ 'find': r'code FAR * FAR *', 'replace': r'typedef code FAR * FAR *' },
]
},
}
source = [
'zlib/adler32.c',
'zlib/compress.c',
'zlib/crc32.c',
'zlib/deflate.c',
'zlib/gzclose.c',
'zlib/gzlib.c',
'zlib/gzread.c',
'zlib/gzwrite.c',
'zlib/infback.c',
'zlib/inffast.c',
'zlib/inflate.c',
'zlib/inftrees.c',
'zlib/trees.c',
'zlib/uncompr.c',
'zlib/zutil.c',
# Nested headers
'zlib/crc32.h',
'zlib/trees.h',
]
source_extra_operations = {
'zlib/gzread.c' : {
'findReplace' : [
{ 'find': 'GZIP', 'replace': 'GZIPSTREAM' } ,
],
},
'zlib/inflate.c' : {
'findReplace' : [
{ 'find': r'COPY([^_])', 'replace': r'COPY__\1' },
{ 'find': r'PULLBYTE', 'replace': r'PULLBYTE_INFLATE' },
{ 'find': r'NEEDBITS', 'replace': r'NEEDBITS_INFLATE' },
],
'remove_lines' : [
{ 'begin': 261, 'end': 302 },
],
},
'zlib/trees.c' : {
'findReplace' : [
{ 'find': r'bi_reverse\(code, len\)', 'replace': r'bi_reverse(unsigned code, int len)' },
],
'remove_lines' : [
{ 'begin': 1155, 'end': 1156 },
],
},
'zlib/crc32.c' : {
'findReplace' : [
{ 'find': r'DO1', 'replace': r'DO1_CRC32' },
{ 'find': r'DO8', 'replace': r'DO8_CRC32' },
],
},
}
included_paths = set()
# Amalgamate header files
config = {}
config['amalgamation_header'] = list(amalgamation_header)
config['amalgamation_header'] += ['#define NO_DUMMY_DECL // Disable "Buggy compiler workaround" in amalgamation']
config['amalgamation_header'] += ['','','']
config['file_header'] = file_header
config['file_footer'] = file_footer
config['input'] = headers
config['output'] = 'zlib_amalg.h'
config['include_mask'] = headers
config['extra_operations'] = include_extra_operations
config['include_root'] = os.path.dirname(os.path.realpath(__file__))
config['allow_nesting'] = False
fts_amalgamate(config, included_paths)
# Amalgamate source files
config = {}
config['amalgamation_header'] = list(amalgamation_header)
config['amalgamation_header'] += ['#include "zlib_amalg.h"']
config['amalgamation_header'] += ['','','']
config['file_header'] = file_header
config['file_footer'] = file_footer
config['input'] = source
config['output'] = 'zlib_amalg.c'
config['include_mask'] = headers + ['zlib/crc32.h', 'zlib/trees.h'] # Amalgamated headers plus nested headers
config['extra_operations'] = source_extra_operations
config['include_root'] = os.path.dirname(os.path.realpath(__file__))
config['allow_nesting'] = True
fts_amalgamate(config, included_paths)
if __name__ == '__main__':
main()