1#!/bin/python3 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, str): 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.items(): 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", encoding="utf-8") as mk: 77 m.update(mk.read().encode("utf-8")) 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 ) 90 make.append("set(%s_src %s)" % (name, " ".join(module["src"]))) 91 if module["type"] == "SHARED_LIBRARY": 92 make.append( 93 "android_add_library(TARGET {} SHARED LICENSE Apache-2.0 SRC {})".format( 94 name, " ".join(module["src"]) 95 ) 96 ) 97 elif module["type"] == "STATIC_LIBRARY": 98 make.append( 99 "android_add_library(TARGET {} LICENSE Apache-2.0 SRC {})".format( 100 name, " ".join(module["src"]) 101 ) 102 ) 103 else: 104 raise ValueError("Unexpected module type: %s" % module["type"]) 105 106 # Fix up the includes. 107 includes = ["${GOLDFISH_DEVICE_ROOT}/" + s for s in module["includes"]] 108 make.append( 109 "target_include_directories(%s PRIVATE %s)" % (name, " ".join(includes)) 110 ) 111 112 # filter out definitions 113 defs = [escape(d) for d in module["cflags"] if d.startswith("-D")] 114 115 # And the remaining flags. 116 flags = [escape(d) for d in module["cflags"] if not d.startswith("-D")] 117 118 # Make sure we remove the lib prefix from all our dependencies. 119 libs = [remove_lib_prefix(l) for l in module.get("libs", [])] 120 staticlibs = [ 121 remove_lib_prefix(l) 122 for l in module.get("staticlibs", []) 123 if l != "libandroidemu" 124 ] 125 126 # Configure the target. 127 make.append("target_compile_definitions(%s PRIVATE %s)" % (name, " ".join(defs))) 128 make.append("target_compile_options(%s PRIVATE %s)" % (name, " ".join(flags))) 129 130 if len(staticlibs) > 0: 131 make.append( 132 "target_link_libraries(%s PRIVATE %s PRIVATE %s)" 133 % (name, " ".join(libs), " ".join(staticlibs)) 134 ) 135 else: 136 make.append("target_link_libraries(%s PRIVATE %s)" % (name, " ".join(libs))) 137 return make 138 139 140def main(argv=None): 141 parser = argparse.ArgumentParser( 142 description="Generates a set of cmake files" 143 "based up the js representation." 144 "Use this to generate cmake files that can be consumed by the emulator build" 145 ) 146 parser.add_argument( 147 "-i", 148 "--input", 149 dest="input", 150 type=str, 151 required=True, 152 help="json file containing the build tree", 153 ) 154 parser.add_argument( 155 "-v", 156 "--verbose", 157 action="store_const", 158 dest="loglevel", 159 const=logging.INFO, 160 default=logging.ERROR, 161 help="Log what is happening", 162 ) 163 parser.add_argument( 164 "-o", 165 "--output", 166 dest="outdir", 167 type=str, 168 default=None, 169 help="Output directory for create CMakefile.txt", 170 ) 171 parser.add_argument( 172 "-c", 173 "--clean", 174 dest="output", 175 type=str, 176 default=None, 177 help="Write out the cleaned up js", 178 ) 179 args = parser.parse_args() 180 181 logging.basicConfig(level=args.loglevel) 182 183 with open(args.input) as data_file: 184 data = json.load(data_file) 185 186 modules = cleanup_json(data) 187 188 # Write out cleaned up json, mainly useful for debugging etc. 189 if args.output is not None: 190 with open(args.output, "w") as out_file: 191 out_file.write(json.dumps(modules, indent=2)) 192 193 # Location --> CMakeLists.txt 194 cmake = {} 195 196 # The root, it will basically just include all the generated files. 197 root = os.path.join(args.outdir, "CMakeLists.txt") 198 mkfile = os.path.join(args.outdir, "Android.mk") 199 sha256 = checksum(mkfile) 200 cmake[root] = header() 201 cmake[root].append("set(GOLDFISH_DEVICE_ROOT ${CMAKE_CURRENT_SOURCE_DIR})") 202 cmake[root].append( 203 'android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/%s" "%s")' % (mkfile, sha256) 204 ) 205 206 # Generate the modules. 207 for module in modules: 208 location = os.path.join(args.outdir, module["path"], "CMakeLists.txt") 209 210 # Make sure we handle the case where we have >2 modules in the same dir. 211 if location not in cmake: 212 path = module["path"] 213 if path.startswith("../"): 214 path_binary_dir = path 215 path_binary_dir = path_binary_dir.replace("../", "") 216 path_binary_dir = path_binary_dir.replace("/", "-") 217 path_binary_dir = path_binary_dir.lower() 218 219 cmake[root].append("add_subdirectory(%s %s)" % (path, path_binary_dir)) 220 else: 221 cmake[root].append("add_subdirectory(%s)" % path) 222 cmake[location] = [] 223 cmake[location].extend(generate_module(module)) 224 225 # Write them to disk. 226 for (loc, cmklist) in cmake.items(): 227 logging.info("Writing to %s", loc) 228 with open(loc, "w") as fn: 229 fn.write("\n".join(cmklist)) 230 231 232if __name__ == "__main__": 233 sys.exit(main()) 234