| #!/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()) |