123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- import argparse
- import sys, os
- import base64
- import hashlib
- import tempfile
- import shutil
- import subprocess
- from collections import namedtuple
- def call(cmd, cwd=None, env=None):
- return subprocess.check_output(cmd, stdin=None, stderr=subprocess.STDOUT, cwd=cwd, env=env)
- class LLVMResourceInserter:
- def __init__(self, args, obj_output):
- self.cxx = args.compiler
- self.objcopy = args.objcopy
- self.rescompiler = args.rescompiler
- self.compressor = args.compressor
- self.obj_out = obj_output
- has_target = args.target or args.target != '__unknown_target__'
- self.target_flags = [f"--target={args.target}"] if has_target else []
- seed = os.path.basename(obj_output)
- self.mangler = lambda key: 'R' + hashlib.sha256((seed + key).encode()).hexdigest()
- self.tmpdir = tempfile.mkdtemp()
- def __enter__(self):
- return self
- def __exit__(self, *_):
- shutil.rmtree(self.tmpdir)
- PackedArgs = namedtuple("PackedArgs", ["infile_path", "comressed_file_path", "symbol_name"])
- def _insert_files(self, infos: list[PackedArgs], output_obj: str) -> None:
- if len(infos) == 0:
- return
- # Compress resources
- cmd = [self.compressor, "--compress-only"]
- for inserted_file, compressed_file, _ in infos:
- cmd.extend([inserted_file, compressed_file])
- call(cmd)
- # Put resources into .o file
- infos = [(zipped_file, os.path.getsize(zipped_file), symbol_name) for (_, zipped_file, symbol_name) in infos]
- cmd = [self.objcopy]
- # NOTE: objcopy does not distinguish the order of arguments.
- for fname, fsize, sym in infos:
- section_name = f'.rodata.{sym}'
- cmd.extend(
- [
- f'--add-section={section_name}={fname}',
- f'--set-section-flags={section_name}=readonly',
- f'--add-symbol={sym}={section_name}:0,global',
- f'--add-symbol={sym}_end={section_name}:{fsize},global',
- ]
- )
- cmd.extend([output_obj])
- call(cmd)
- @staticmethod
- def flat_merge_cpp(outs, output):
- if len(outs) == 1:
- shutil.move(outs[0], output)
- return
- with open(output, 'w') as fout:
- for fname in outs:
- with open(fname, 'r') as fin:
- shutil.copyfileobj(fin, fout)
- return
- def insert_resources(self, kv_files, kv_strings):
- kv_files = list(kv_files)
- # Generate resource registration cpp code & compile it
- with tempfile.NamedTemporaryFile(suffix='.cc') as dummy_src:
- cmd = [self.rescompiler, dummy_src.name, '--use-sections']
- for path, key in kv_files + list(('-', k) for k in kv_strings):
- if path != '-':
- path = self.mangler(key)
- cmd.extend([path, key])
- call(cmd)
- # Compile
- call([self.cxx, dummy_src.name, *self.target_flags, '-c', '-o', self.obj_out])
- # Put files
- infos = [[]]
- estimated_cmd_len = 0
- LIMIT = 6000
- for idx, (path, key) in enumerate(kv_files):
- packed_args = (path, os.path.join(self.tmpdir, f'{idx}.zstd'), self.mangler(key))
- infos[-1].append(packed_args)
- estimated_cmd_len += len(path)
- if estimated_cmd_len > LIMIT:
- infos.append([])
- estimated_cmd_len = 0
- for packed_args in infos:
- self._insert_files(packed_args, self.obj_out)
- return self.obj_out
- def parse_args():
- parser = argparse.ArgumentParser()
- parser.add_argument('--compiler', required=True)
- parser.add_argument('--objcopy', required=True)
- parser.add_argument('--compressor', required=True)
- parser.add_argument('--rescompiler', required=True)
- parser.add_argument('--output_obj', required=True)
- parser.add_argument('--inputs', nargs='+', required=False, default=[])
- parser.add_argument('--keys', nargs='+', required=False, default=[])
- parser.add_argument('--kvs', nargs='+', required=False, default=[])
- parser.add_argument('--target', required=True)
- args = parser.parse_args()
- # Decode hex to original string
- args.keys = list(base64.b64decode(it).decode("utf-8") for it in args.keys)
- return args, args.inputs, args.keys
- def main():
- args, inputs, keys = parse_args()
- with LLVMResourceInserter(args, args.output_obj) as inserter:
- inserter.insert_resources(zip(inputs, keys), args.kvs)
- return
- if __name__ == '__main__':
- main()
|