#!/bin/python3 import argparse import hashlib import json import logging import os import sys def cleanup_json(data): """Cleans up the json structure by removing empty "", and empty key value pairs.""" if isinstance(data, str): copy = data.strip() return None if len(copy) == 0 else copy if isinstance(data, dict): copy = {} for key, value in data.items(): rem = cleanup_json(value) if rem is not None: copy[key] = rem return None if len(copy) == 0 else copy if isinstance(data, list): copy = [] for elem in data: rem = cleanup_json(elem) if rem is not None: if rem not in copy: copy.append(rem) if len(copy) == 0: return None return copy class AttrDict(dict): def __init__(self, *args, **kwargs): super(AttrDict, self).__init__(*args, **kwargs) self.__dict__ = self def as_list(self, name): v = self.get(name, []) if isinstance(v, list): return v return [v] def remove_lib_prefix(module): """Removes the lib prefix, as we are not using them in CMake.""" if module.startswith("lib"): return module[3:] else: return module def escape(msg): """Escapes the ".""" return '"' + msg.replace('"', '\\"') + '"' def header(): """The auto generate header.""" return [ "# This is an autogenerated file! Do not edit!", "# instead run make from .../device/generic/goldfish-opengl", "# which will re-generate this file.", ] def checksum(fname): """Calculates a SHA256 digest of the given file name.""" m = hashlib.sha256() with open(fname, "r", encoding="utf-8") as mk: m.update(mk.read().encode("utf-8")) return m.hexdigest() def generate_module(module): """Generates a cmake module.""" name = remove_lib_prefix(module["module"]) make = header() mkfile = os.path.join(module["path"], "Android.mk") sha256 = checksum(mkfile) make.append( 'android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/%s" "%s")' % (mkfile, sha256) ) make.append("set(%s_src %s)" % (name, " ".join(module["src"]))) if module["type"] == "SHARED_LIBRARY": make.append( "android_add_library(TARGET {} SHARED LICENSE Apache-2.0 SRC {})".format( name, " ".join(module["src"]) ) ) elif module["type"] == "STATIC_LIBRARY": make.append( "android_add_library(TARGET {} LICENSE Apache-2.0 SRC {})".format( name, " ".join(module["src"]) ) ) else: raise ValueError("Unexpected module type: %s" % module["type"]) # Fix up the includes. includes = ["${GOLDFISH_DEVICE_ROOT}/" + s for s in module["includes"]] make.append( "target_include_directories(%s PRIVATE %s)" % (name, " ".join(includes)) ) # filter out definitions defs = [escape(d) for d in module["cflags"] if d.startswith("-D")] # And the remaining flags. flags = [escape(d) for d in module["cflags"] if not d.startswith("-D")] # Make sure we remove the lib prefix from all our dependencies. libs = [remove_lib_prefix(l) for l in module.get("libs", [])] staticlibs = [ remove_lib_prefix(l) for l in module.get("staticlibs", []) if l != "libandroidemu" ] # Configure the target. make.append("target_compile_definitions(%s PRIVATE %s)" % (name, " ".join(defs))) make.append("target_compile_options(%s PRIVATE %s)" % (name, " ".join(flags))) if len(staticlibs) > 0: make.append( "target_link_libraries(%s PRIVATE %s PRIVATE %s)" % (name, " ".join(libs), " ".join(staticlibs)) ) else: make.append("target_link_libraries(%s PRIVATE %s)" % (name, " ".join(libs))) return make def main(argv=None): parser = argparse.ArgumentParser( description="Generates a set of cmake files" "based up the js representation." "Use this to generate cmake files that can be consumed by the emulator build" ) parser.add_argument( "-i", "--input", dest="input", type=str, required=True, help="json file containing the build tree", ) parser.add_argument( "-v", "--verbose", action="store_const", dest="loglevel", const=logging.INFO, default=logging.ERROR, help="Log what is happening", ) parser.add_argument( "-o", "--output", dest="outdir", type=str, default=None, help="Output directory for create CMakefile.txt", ) parser.add_argument( "-c", "--clean", dest="output", type=str, default=None, help="Write out the cleaned up js", ) args = parser.parse_args() logging.basicConfig(level=args.loglevel) with open(args.input) as data_file: data = json.load(data_file) modules = cleanup_json(data) # Write out cleaned up json, mainly useful for debugging etc. if args.output is not None: with open(args.output, "w") as out_file: out_file.write(json.dumps(modules, indent=2)) # Location --> CMakeLists.txt cmake = {} # The root, it will basically just include all the generated files. root = os.path.join(args.outdir, "CMakeLists.txt") mkfile = os.path.join(args.outdir, "Android.mk") sha256 = checksum(mkfile) cmake[root] = header() cmake[root].append("set(GOLDFISH_DEVICE_ROOT ${CMAKE_CURRENT_SOURCE_DIR})") cmake[root].append( 'android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/%s" "%s")' % (mkfile, sha256) ) # Generate the modules. for module in modules: location = os.path.join(args.outdir, module["path"], "CMakeLists.txt") # Make sure we handle the case where we have >2 modules in the same dir. if location not in cmake: cmake[root].append("add_subdirectory(%s)" % module["path"]) cmake[location] = [] cmake[location].extend(generate_module(module)) # Write them to disk. for (loc, cmklist) in cmake.items(): logging.info("Writing to %s", loc) with open(loc, "w") as fn: fn.write("\n".join(cmklist)) if __name__ == "__main__": sys.exit(main())