1#!/bin/python 2import argparse 3import hashlib 4import json 5import logging 6import os 7import sys 8 9 10def cleanup_json(data): 11 """Cleans up the json structure by removing empty "", and empty key value 12 pairs.""" 13 if (isinstance(data, unicode)): 14 copy = data.strip() 15 return None if len(copy) == 0 else copy 16 17 if (isinstance(data, dict)): 18 copy = {} 19 for key, value in data.iteritems(): 20 rem = cleanup_json(value) 21 if (rem is not None): 22 copy[key] = rem 23 return None if len(copy) == 0 else copy 24 25 if (isinstance(data, list)): 26 copy = [] 27 for elem in data: 28 rem = cleanup_json(elem) 29 if (rem is not None): 30 if rem not in copy: 31 copy.append(rem) 32 33 if len(copy) == 0: 34 return None 35 return copy 36 37 38class AttrDict(dict): 39 def __init__(self, *args, **kwargs): 40 super(AttrDict, self).__init__(*args, **kwargs) 41 self.__dict__ = self 42 43 def as_list(self, name): 44 v = self.get(name, []) 45 if (isinstance(v, list)): 46 return v 47 48 return [v] 49 50 51def remove_lib_prefix(module): 52 """Removes the lib prefix, as we are not using them in CMake.""" 53 if module.startswith('lib'): 54 return module[3:] 55 else: 56 return module 57 58 59def escape(msg): 60 """Escapes the ".""" 61 return '"' + msg.replace('"', '\\"') + '"' 62 63 64def header(): 65 """The auto generate header.""" 66 return [ 67 '# This is an autogenerated file! Do not edit!', 68 '# instead run make from .../device/generic/goldfish-opengl', 69 '# which will re-generate this file.' 70 ] 71 72 73def checksum(fname): 74 """Calculates a SHA256 digest of the given file name.""" 75 m = hashlib.sha256() 76 with open(fname, 'r') as mk: 77 m.update(mk.read()) 78 return m.hexdigest() 79 80 81def generate_module(module): 82 """Generates a cmake module.""" 83 name = remove_lib_prefix(module['module']) 84 make = header() 85 mkfile = os.path.join(module['path'], 'Android.mk') 86 sha256 = checksum(mkfile) 87 make.append( 88 'android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/%s" "%s")' % (mkfile, sha256)) 89 make.append('set(%s_src %s)' % (name, ' '.join(module['src']))) 90 if module['type'] == 'SHARED_LIBRARY': 91 make.append('android_add_library(TARGET {} SHARED LICENSE Apache-2.0 SRC {})'.format(name, ' '.join(module['src']))) 92 elif module['type'] == 'STATIC_LIBRARY': 93 make.append('android_add_library(TARGET {} LICENSE Apache-2.0 SRC {})'.format(name, ' '.join(module['src']))) 94 else: 95 raise ValueError('Unexpected module type: %s' % module['type']) 96 97 # Fix up the includes. 98 includes = ['${GOLDFISH_DEVICE_ROOT}/' + s for s in module['includes']] 99 make.append('target_include_directories(%s PRIVATE %s)' % 100 (name, ' '.join(includes))) 101 102 # filter out definitions 103 defs = [escape(d) for d in module['cflags'] if d.startswith('-D')] 104 105 # And the remaining flags. 106 flags = [escape(d) for d in module['cflags'] if not d.startswith('-D')] 107 108 # Make sure we remove the lib prefix from all our dependencies. 109 libs = [remove_lib_prefix(l) for l in module.get('libs', [])] 110 staticlibs = [remove_lib_prefix(l) for l in 111 module.get('staticlibs', []) 112 if l != "libandroidemu"] 113 114 # Configure the target. 115 make.append('target_compile_definitions(%s PRIVATE %s)' % 116 (name, ' '.join(defs))) 117 make.append('target_compile_options(%s PRIVATE %s)' % 118 (name, ' '.join(flags))) 119 120 if len(staticlibs) > 0: 121 make.append('target_link_libraries(%s PRIVATE %s PRIVATE %s)' % 122 (name, ' '.join(libs), " ".join(staticlibs))) 123 else: 124 make.append('target_link_libraries(%s PRIVATE %s)' % 125 (name, ' '.join(libs))) 126 return make 127 128 129def main(argv=None): 130 parser = argparse.ArgumentParser( 131 description='Generates a set of cmake files' 132 'based up the js representation.' 133 'Use this to generate cmake files that can be consumed by the emulator build') 134 parser.add_argument('-i', '--input', dest='input', type=str, required=True, 135 help='json file containing the build tree') 136 parser.add_argument('-v', '--verbose', 137 action='store_const', dest='loglevel', 138 const=logging.INFO, default=logging.ERROR, 139 help='Log what is happening') 140 parser.add_argument('-o', '--output', 141 dest='outdir', type=str, default=None, 142 help='Output directory for create CMakefile.txt') 143 parser.add_argument('-c', '--clean', dest='output', type=str, 144 default=None, 145 help='Write out the cleaned up js') 146 args = parser.parse_args() 147 148 logging.basicConfig(level=args.loglevel) 149 150 with open(args.input) as data_file: 151 data = json.load(data_file) 152 153 modules = cleanup_json(data) 154 155 # Write out cleaned up json, mainly useful for debugging etc. 156 if (args.output is not None): 157 with open(args.output, 'w') as out_file: 158 out_file.write(json.dumps(modules, indent=2)) 159 160 # Location --> CMakeLists.txt 161 cmake = {} 162 163 # The root, it will basically just include all the generated files. 164 root = os.path.join(args.outdir, 'CMakeLists.txt') 165 mkfile = os.path.join(args.outdir, 'Android.mk') 166 sha256 = checksum(mkfile) 167 cmake[root] = header() 168 cmake[root].append('set(GOLDFISH_DEVICE_ROOT ${CMAKE_CURRENT_SOURCE_DIR})') 169 cmake[root].append( 170 'android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/%s" "%s")' % (mkfile, sha256)) 171 172 # Generate the modules. 173 for module in modules: 174 location = os.path.join(args.outdir, module['path'], 'CMakeLists.txt') 175 176 # Make sure we handle the case where we have >2 modules in the same dir. 177 if location not in cmake: 178 cmake[root].append('add_subdirectory(%s)' % module['path']) 179 cmake[location] = [] 180 cmake[location].extend(generate_module(module)) 181 182 # Write them to disk. 183 for (loc, cmklist) in cmake.iteritems(): 184 logging.info('Writing to %s', loc) 185 with open(loc, 'w') as fn: 186 fn.write('\n'.join(cmklist)) 187 188 189if __name__ == '__main__': 190 sys.exit(main()) 191