Skip to content

Instantly share code, notes, and snippets.

@revsic
Created August 9, 2019 10:36
Show Gist options
  • Save revsic/78d9372fe9e0dd1f7a1decff40bd79a2 to your computer and use it in GitHub Desktop.
Save revsic/78d9372fe9e0dd1f7a1decff40bd79a2 to your computer and use it in GitHub Desktop.
Python implementation of C++ headeronly lib generator
import os
import re
import sys
class Format(object):
hpp = '''\
#ifndef {0}
#define {0}
{1}
{2}
{3}
#endif'''
@classmethod
def hpp_beutifier(cls, source):
out = ''
n_blank = 0
for line in source.split('\n'):
if line == '' or line.isspace():
n_blank += 1
else:
if n_blank > 2:
n_blank = 2
out += '\n' * n_blank
n_blank = 0
out += line + '\n'
return out
class ReSupport(object):
r_guard = re.compile(r'(#ifndef|#endif)')
r_include_dep = re.compile(r'#include "(.+?)"')
r_include = re.compile(r'#include <(.+?)>')
r_define = re.compile(r'#define (.+)')
@classmethod
def guard(cls, inp):
return cls.r_guard.findall(inp)
@classmethod
def include_dep(cls, inp):
return cls.r_include_dep.findall(inp)
@classmethod
def include(cls, inp):
return cls.r_include.findall(inp)
@classmethod
def define(cls, inp):
return cls.r_define.findall(inp)
class SourceInfo(object):
def __init__(self):
self.out = ''
self.deps = []
self.includes = []
self.defines = []
@classmethod
def set_with(cls, out, deps, includes, defines):
obj = cls()
obj.out = out
obj.deps = deps
obj.includes = includes
obj.defines = defines
return obj
@classmethod
def read_file(cls, path):
obj = cls()
with open(path) as f:
for line in f.readlines():
if len(ReSupport.guard(line)) > 0:
continue
include_name = ReSupport.include_dep(line)
if len(include_name) > 0:
obj.deps.append(include_name[0])
continue
include_name = ReSupport.include(line)
if len(include_name) > 0:
obj.includes.append(include_name[0])
continue
define_name = ReSupport.define(line)
if len(define_name) > 0:
obj.defines.append(define_name[0])
continue
obj.out += line
return obj
def __add__(self, other):
out = self.out + other.out
deps = self.deps + other.deps
includes = self.includes + other.includes
defines = self.defines + other.defines
return SourceInfo.set_with(out, deps, includes, defines)
def file_list(dirname, ban_dir=[]):
files = []
if isinstance(ban_dir, str):
ban_dir = [ban_dir]
for name in os.listdir(dirname):
full_path = os.path.join(dirname, name)
if os.path.isfile(full_path):
files.append(full_path)
elif os.path.isdir(full_path) and name not in ban_dir:
files += file_list(full_path)
return files
def in_endswith(name, files):
for f in files:
if f.endswith(name):
return f
return None
def order_dep(deps, files, done):
info = SourceInfo()
for dep in deps:
if in_endswith(dep, done) is None:
path = in_endswith(dep.split('/')[-1], files)
if path is not None:
new = SourceInfo.read_file(path)
dep_new = order_dep(new.deps, files, done)
info += dep_new + new
done.append(path)
return info
def none_preproc(dirname):
if not os.path.exists(dirname):
return [], ''
dep = []
out = ''
includes = ''
for files in os.listdir(dirname):
with open(os.path.join(dirname, files)) as f:
lines = f.readlines()
if lines[0].startswith('#ifndef') \
and lines[1].startswith('#define') \
and lines[-1].startswith('#endif'):
lines = lines[2:-1]
data = ''.join(lines)
include_idx = data.find('// merge:np_include')
if include_idx != -1:
start_idx = include_idx + len('// merge:np_include')
end_idx = data.find('// merge:end', include_idx)
includes += data[start_idx:end_idx]
data = data[end_idx + len('// merge:end'):]
include_idx = data.find('// merge:include')
if include_idx != -1:
start_idx = include_idx + len('// merge:include')
end_idx = data.find('// merge:end', include_idx)
for line in data[start_idx:end_idx].split('\n'):
res = ReSupport.include(line)
if len(res) > 0:
dep += res
includes += line + '\n'
data = data[end_idx + len('// merge:end'):]
dep.append(files)
out += data
return dep, includes + out
def merge(dirname):
done = []
info = SourceInfo()
files = file_list(dirname, 'platform')
preproc_dep, info.out = \
none_preproc(os.path.join(dirname, 'platform'))
for full_path in files:
if full_path not in done:
source = SourceInfo.read_file(full_path)
dep = order_dep(source.deps, files, done)
info += dep + source
done.append(full_path)
return info, preproc_dep
def write_hpp(outfile, merged):
info, preproc_dep = merged
dep_check = lambda file: not any(file.endswith(dep) for dep in preproc_dep)
idx = outfile.rfind('/')
if idx > -1:
outfile = outfile[idx+1:]
guard_name = outfile.upper().replace('.', '_')
unique_include = []
for include in info.includes:
if include not in unique_include and dep_check(include):
unique_include.append(include)
includes = '\n'.join(
'#include <{}>'.format(x) for x in sorted(unique_include)) + '\n'
defines = '\n'.join(
'#define {}'.format(x) for x in info.defines) + '\n'
out = Format.hpp.format(guard_name, includes, defines, info.out)
out = Format.hpp_beutifier(out)
with open(outfile, 'w') as f:
f.write(out)
if __name__ == "__main__":
if len(sys.argv) > 1:
outfile = sys.argv[1]
else:
outfile = './obfuscator.hpp'
if len(sys.argv) > 2:
dirname = sys.argv[2]
else:
dirname = './obfuscator/obfs'
merged = merge(dirname)
write_hpp(outfile, merged)
@revsic
Copy link
Author

revsic commented Aug 9, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment