1#!/usr/bin/python 2# -*- coding: utf-8 -*- 3# Tool to embed file into objects 4 5__author__ = __maintainer__ = "Jérôme Carretero <cJ-waf@zougloub.eu>" 6__copyright__ = "Jérôme Carretero, 2014" 7 8""" 9 10This tool allows to embed file contents in object files (.o). 11It is not exactly portable, and the file contents are reachable 12using various non-portable fashions. 13The goal here is to provide a functional interface to the embedding 14of file data in objects. 15See the ``playground/embedded_resources`` example for an example. 16 17Usage:: 18 19 bld( 20 name='pipeline', 21 # ^ Reference this in use="..." for things using the generated code 22 features='file_to_object', 23 source='some.file', 24 # ^ Name of the file to embed in binary section. 25 ) 26 27Known issues: 28 29- Destination is named like source, with extension renamed to .o 30 eg. some.file -> some.o 31 32""" 33 34import os, sys 35from waflib import Task, TaskGen, Errors 36 37def filename_c_escape(x): 38 return x.replace("\\", "\\\\") 39 40class file_to_object_s(Task.Task): 41 color = 'CYAN' 42 vars = ['DEST_CPU', 'DEST_BINFMT'] 43 44 def run(self): 45 name = [] 46 for i, x in enumerate(self.inputs[0].name): 47 if x.isalnum(): 48 name.append(x) 49 else: 50 name.append('_') 51 file = self.inputs[0].abspath() 52 size = os.path.getsize(file) 53 if self.env.DEST_CPU in ('x86_64', 'ia', 'aarch64'): 54 unit = 'quad' 55 align = 8 56 elif self.env.DEST_CPU in ('x86','arm', 'thumb', 'm68k'): 57 unit = 'long' 58 align = 4 59 else: 60 raise Errors.WafError("Unsupported DEST_CPU, please report bug!") 61 62 file = filename_c_escape(file) 63 name = "_binary_" + "".join(name) 64 rodata = ".section .rodata" 65 if self.env.DEST_BINFMT == "mac-o": 66 name = "_" + name 67 rodata = ".section __TEXT,__const" 68 69 with open(self.outputs[0].abspath(), 'w') as f: 70 f.write(\ 71""" 72 .global %(name)s_start 73 .global %(name)s_end 74 .global %(name)s_size 75 %(rodata)s 76%(name)s_start: 77 .incbin "%(file)s" 78%(name)s_end: 79 .align %(align)d 80%(name)s_size: 81 .%(unit)s 0x%(size)x 82""" % locals()) 83 84class file_to_object_c(Task.Task): 85 color = 'CYAN' 86 def run(self): 87 name = [] 88 for i, x in enumerate(self.inputs[0].name): 89 if x.isalnum(): 90 name.append(x) 91 else: 92 name.append('_') 93 file = self.inputs[0].abspath() 94 size = os.path.getsize(file) 95 96 name = "_binary_" + "".join(name) 97 98 def char_to_num(ch): 99 if sys.version_info[0] < 3: 100 return ord(ch) 101 return ch 102 103 data = self.inputs[0].read('rb') 104 lines, line = [], [] 105 for idx_byte, byte in enumerate(data): 106 line.append(byte) 107 if len(line) > 15 or idx_byte == size-1: 108 lines.append(", ".join(("0x%02x" % char_to_num(x)) for x in line)) 109 line = [] 110 data = ",\n ".join(lines) 111 112 self.outputs[0].write(\ 113""" 114unsigned long %(name)s_size = %(size)dL; 115char const %(name)s_start[] = { 116 %(data)s 117}; 118char const %(name)s_end[] = {}; 119""" % locals()) 120 121@TaskGen.feature('file_to_object') 122@TaskGen.before_method('process_source') 123def tg_file_to_object(self): 124 bld = self.bld 125 sources = self.to_nodes(self.source) 126 targets = [] 127 for src in sources: 128 if bld.env.F2O_METHOD == ["asm"]: 129 tgt = src.parent.find_or_declare(src.name + '.f2o.s') 130 tsk = self.create_task('file_to_object_s', src, tgt) 131 tsk.cwd = src.parent.abspath() # verify 132 else: 133 tgt = src.parent.find_or_declare(src.name + '.f2o.c') 134 tsk = self.create_task('file_to_object_c', src, tgt) 135 tsk.cwd = src.parent.abspath() # verify 136 targets.append(tgt) 137 self.source = targets 138 139def configure(conf): 140 conf.load('gas') 141 conf.env.F2O_METHOD = ["c"] 142 143