Erwin Jansen | f96db3c | 2018-10-25 23:28:54 -0700 | [diff] [blame] | 1 | #!/bin/python |
| 2 | import argparse |
Erwin Jansen | a7916b1 | 2018-10-26 09:44:10 -0700 | [diff] [blame^] | 3 | import hashlib |
Erwin Jansen | f96db3c | 2018-10-25 23:28:54 -0700 | [diff] [blame] | 4 | import json |
| 5 | import logging |
| 6 | import os |
| 7 | import sys |
| 8 | |
| 9 | |
| 10 | def 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 | |
| 38 | class 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 | |
| 51 | def 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 | |
| 59 | def escape(msg): |
| 60 | """Escapes the ".""" |
| 61 | return '"' + msg.replace('"', '\\"') + '"' |
| 62 | |
| 63 | |
| 64 | def 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 | |
Erwin Jansen | a7916b1 | 2018-10-26 09:44:10 -0700 | [diff] [blame^] | 73 | def 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 | |
Erwin Jansen | f96db3c | 2018-10-25 23:28:54 -0700 | [diff] [blame] | 81 | def generate_module(module): |
| 82 | """Generates a cmake module.""" |
| 83 | name = remove_lib_prefix(module['module']) |
| 84 | make = header() |
Erwin Jansen | a7916b1 | 2018-10-26 09:44:10 -0700 | [diff] [blame^] | 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)) |
Erwin Jansen | f96db3c | 2018-10-25 23:28:54 -0700 | [diff] [blame] | 89 | make.append('set(%s_src %s)' % (name, ' '.join(module['src']))) |
| 90 | if module['type'] == 'SHARED_LIBRARY': |
| 91 | make.append('android_add_shared_library(%s)' % name) |
Lingfeng Yang | d744704 | 2018-10-25 11:09:18 -0700 | [diff] [blame] | 92 | elif module['type'] == 'STATIC_LIBRARY': |
| 93 | make.append('android_add_library(%s)' % name) |
Erwin Jansen | f96db3c | 2018-10-25 23:28:54 -0700 | [diff] [blame] | 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['libs']] |
Lingfeng Yang | f4d77ef | 2018-11-02 23:21:37 -0700 | [diff] [blame] | 110 | staticlibs = [remove_lib_prefix(l) for l in |
| 111 | module.get('staticlibs', []) |
| 112 | if l != "libandroidemu"] |
Erwin Jansen | f96db3c | 2018-10-25 23:28:54 -0700 | [diff] [blame] | 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))) |
Lingfeng Yang | f4d77ef | 2018-11-02 23:21:37 -0700 | [diff] [blame] | 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))) |
Erwin Jansen | f96db3c | 2018-10-25 23:28:54 -0700 | [diff] [blame] | 126 | return make |
| 127 | |
| 128 | |
| 129 | def 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') |
Erwin Jansen | a7916b1 | 2018-10-26 09:44:10 -0700 | [diff] [blame^] | 165 | mkfile = os.path.join(args.outdir, 'Android.mk') |
| 166 | sha256 = checksum(mkfile) |
Erwin Jansen | f96db3c | 2018-10-25 23:28:54 -0700 | [diff] [blame] | 167 | cmake[root] = header() |
| 168 | cmake[root].append('set(GOLDFISH_DEVICE_ROOT ${CMAKE_CURRENT_SOURCE_DIR})') |
Erwin Jansen | a7916b1 | 2018-10-26 09:44:10 -0700 | [diff] [blame^] | 169 | cmake[root].append( |
| 170 | 'android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/%s" "%s")' % (mkfile, sha256)) |
Erwin Jansen | f96db3c | 2018-10-25 23:28:54 -0700 | [diff] [blame] | 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 | |
| 189 | if __name__ == '__main__': |
| 190 | sys.exit(main()) |