|
| 1 | +import json |
| 2 | +import os |
| 3 | +import stat |
| 4 | +import re |
| 5 | +from collections import OrderedDict |
| 6 | +from subprocess import Popen |
| 7 | + |
| 8 | +git_processes = [] |
| 9 | + |
| 10 | +class MyJSONEncoder(json.JSONEncoder): |
| 11 | + def __init__(self, *args, **kwargs): |
| 12 | + super(MyJSONEncoder, self).__init__(*args, **kwargs) |
| 13 | + self.current_indent = 0 |
| 14 | + self.current_indent_str = "" |
| 15 | + |
| 16 | + |
| 17 | + def encode(self, o): |
| 18 | + #Special Processing for lists |
| 19 | + if isinstance(o, (list, tuple)): |
| 20 | + primitives_only = True |
| 21 | + for item in o: |
| 22 | + if isinstance(item, (list, tuple, dict)): |
| 23 | + primitives_only = False |
| 24 | + break |
| 25 | + output = [] |
| 26 | + if primitives_only: |
| 27 | + for item in o: |
| 28 | + output.append(json.dumps(item)) |
| 29 | + return "[" + ", ".join(output) + "]" |
| 30 | + else: |
| 31 | + self.current_indent += self.indent |
| 32 | + self.current_indent_str = " " * self.current_indent |
| 33 | + for item in o: |
| 34 | + output.append(self.current_indent_str + self.encode(item)) |
| 35 | + self.current_indent -= self.indent |
| 36 | + self.current_indent_str = " " * self.current_indent |
| 37 | + return "[\n" + ",\n".join(output) + "\n" + self.current_indent_str + "]" |
| 38 | + elif isinstance(o, dict): |
| 39 | + primitives_only = True |
| 40 | + for item in o.values(): |
| 41 | + if isinstance(item, (list, tuple, dict)): |
| 42 | + primitives_only = False |
| 43 | + break |
| 44 | + output = [] |
| 45 | + if primitives_only and len(o) < 3: |
| 46 | + for key, value in o.iteritems(): |
| 47 | + output.append(json.dumps(key) + ": " + self.encode(value)) |
| 48 | + return "{" + ", ".join(output) + "}" |
| 49 | + else: |
| 50 | + self.current_indent += self.indent |
| 51 | + self.current_indent_str = " " * self.current_indent |
| 52 | + for key, value in o.iteritems(): |
| 53 | + output.append(self.current_indent_str + json.dumps(key) + ": " + self.encode(value)) |
| 54 | + self.current_indent -= self.indent |
| 55 | + self.current_indent_str = " " * self.current_indent |
| 56 | + return "{\n" + ",\n".join(output) + "\n" + self.current_indent_str + "}" |
| 57 | + else: |
| 58 | + return json.dumps(o) |
| 59 | + |
| 60 | +def load(path): |
| 61 | + with open(path, 'r') as f : |
| 62 | + return json.load(f, object_pairs_hook=OrderedDict) |
| 63 | + |
| 64 | +def dump(path, obj): |
| 65 | + with os.fdopen(os.open(path, os.O_WRONLY | os.O_CREAT, stat.S_IRUSR | stat.S_IWUSR), 'w') as f : |
| 66 | + os.chmod(path, stat.S_IRUSR | stat.S_IWUSR) |
| 67 | + f.write(MyJSONEncoder(indent=4).encode(obj)) |
| 68 | + f.write(u'\n') |
| 69 | + f.truncate() |
| 70 | + |
| 71 | +def find(stem, path) : |
| 72 | + for root, directories, files in os.walk(path, followlinks=True) : |
| 73 | + [dir for dir in directories if dir[0] != '.'] |
| 74 | + if (stem_match(stem,os.path.basename(os.path.normpath(root))) and |
| 75 | + "device.h" in files) : |
| 76 | + return os.path.join(root, "device.h") |
| 77 | + |
| 78 | +def find_all_devices(path, verbose=False) : |
| 79 | + for root, directories, files in os.walk(path, followlinks=True) : |
| 80 | + [dir for dir in directories if dir[0] != '.'] |
| 81 | + if "device.h" in files : |
| 82 | + if verbose : print("[VERBOSE] found a device.h file in {}".format(root)) |
| 83 | + yield os.path.join(root, "device.h") |
| 84 | + |
| 85 | +mbed_matcher = re.compile('mbed', re.IGNORECASE) |
| 86 | +def stem_match(stem, thing) : |
| 87 | + return (stem in thing or |
| 88 | + re.sub(mbed_matcher, '', stem) in thing) |
| 89 | + |
| 90 | +attr_matcher = re.compile('^#define\W+DEVICE_(\w+)\W+1.*$') |
| 91 | +def parse_attributes(path) : |
| 92 | + with open(path) as input : |
| 93 | + for line in input : |
| 94 | + m = re.match(attr_matcher, line) |
| 95 | + if m: yield m.group(1) |
| 96 | + |
| 97 | +def user_select(things, message) : |
| 98 | + print(message) |
| 99 | + for thing, number in zip(things, range(len(things))): |
| 100 | + print("{} : {}".format(number, thing)) |
| 101 | + selection = None |
| 102 | + while selection is None : |
| 103 | + print("please select an integer [0..{}] or specify all".format(len(things) - 1)) |
| 104 | + try : |
| 105 | + i = raw_input() |
| 106 | + if i == "all" : |
| 107 | + selection = "all" |
| 108 | + else : |
| 109 | + selection = int(i) |
| 110 | + if (selection > len(things) or |
| 111 | + selection < 0) : |
| 112 | + print("selection {} out of range".format(selection)) |
| 113 | + selection = None |
| 114 | + except (ValueError, SyntaxError) : |
| 115 | + print("selection not understood") |
| 116 | + if selection == "all" : |
| 117 | + return things |
| 118 | + else : |
| 119 | + return [things[selection]] |
| 120 | + |
| 121 | +target_matcher = re.compile("TARGET_") |
| 122 | +def strip_target(str) : |
| 123 | + return re.sub(target_matcher, "", str) |
| 124 | + |
| 125 | +def add_to_targets(targets, device_file, verbose=False, remove=False) : |
| 126 | + if verbose : print("[VERBOSE] trying target {}".format(device_file)) |
| 127 | + device = strip_target(os.path.basename(os.path.normpath(os.path.dirname(device_file)))) |
| 128 | + if not device : |
| 129 | + print("[WARNING] device {} did not have an associated device.h".format(device)) |
| 130 | + else : |
| 131 | + possible_matches = set([key for key in targets.keys() if stem_match(device, key)]) |
| 132 | + for key, value in targets.iteritems() : |
| 133 | + for alt in value['extra_labels'] if 'extra_labels' in value else [] : |
| 134 | + if stem_match(device, alt) : possible_matches.add(key) |
| 135 | + possible_matches = list(possible_matches) |
| 136 | + for match in possible_matches : |
| 137 | + if device == match : possible_matches = [match] |
| 138 | + if not possible_matches : |
| 139 | + print("[WARNING] device {} did not have an associated entry in targets.json".format(device)) |
| 140 | + return None |
| 141 | + elif len(possible_matches) > 1 : |
| 142 | + message = ("possible matches for file {}".format(device_file)) |
| 143 | + target = user_select(possible_matches, message) |
| 144 | + else : |
| 145 | + target = possible_matches |
| 146 | + attrs = list(parse_attributes(device_file)) |
| 147 | + if attrs : |
| 148 | + for t in target : |
| 149 | + targets[t]["features"] = sorted(list(set(targets[t].setdefault("features",[]) + attrs))) |
| 150 | + if verbose : print("[VERBOSE] target {} now features {}".format(t, attrs)) |
| 151 | + if remove : |
| 152 | + global git_processes |
| 153 | + git = Popen(['git', 'rm', device_file]) |
| 154 | + git_processes.append(git) |
| 155 | + |
| 156 | +if __name__ == '__main__' : |
| 157 | + import argparse |
| 158 | + parser = argparse.ArgumentParser(description='A helpful little script for converting' + |
| 159 | + ' device.h files to parts of the targets.json file') |
| 160 | + parser.add_argument('-a', '--all', action='store_true', |
| 161 | + help='find and convert all available device.h files in the'+ |
| 162 | + ' directory tree starting at the current directory') |
| 163 | + parser.add_argument('-f', '--file', nargs='+', help='specify an individual file to '+ |
| 164 | + 'convert from device.h format to a piece of targets.json') |
| 165 | + parser.add_argument('-t', '--target', nargs='+', help='specify an individual target'+ |
| 166 | + ' to convert from device.h format to a piece of targets.json') |
| 167 | + parser.add_argument('-v', '--verbose', action='store_true', |
| 168 | + help="print out every target that is updated in the targets.json") |
| 169 | + parser.add_argument('-g', '--git-rm', action='store_true', |
| 170 | + help="call git rm on every device.h file") |
| 171 | + args = parser.parse_args() |
| 172 | + if not args.target and not args.file and not args.all : |
| 173 | + print("[WARNING] no action specified; auto-formatting targets.json") |
| 174 | + |
| 175 | + targets_file_name = os.path.join(os.curdir, "hal", "targets.json") |
| 176 | + try : |
| 177 | + targets = load(targets_file_name) |
| 178 | + except OSError : |
| 179 | + print("[ERROR] did not find targets.json where I expected it {}".format(targets_file_name)) |
| 180 | + exit(1) |
| 181 | + except ValueError : |
| 182 | + print("[ERROR] invalid json found in {}".format(targets_file_name)) |
| 183 | + exit(2) |
| 184 | + |
| 185 | + if args.target : |
| 186 | + for target in args.target : |
| 187 | + device_file = find(target, os.curdir) |
| 188 | + if device_file : |
| 189 | + add_to_targets(targets, device_file, verbose=args.verbose, remove=args.git_rm) |
| 190 | + else : |
| 191 | + print("[WARNING] could not locate a device file for target {}".format(target)) |
| 192 | + |
| 193 | + if args.file : |
| 194 | + for file in args.file : |
| 195 | + add_to_targets(targets, file, verbose=args.verbose, remove=args.git_rm) |
| 196 | + |
| 197 | + if args.all : |
| 198 | + for file in find_all_devices(os.curdir, verbose=args.verbose) : |
| 199 | + add_to_targets(targets, file, verbose=args.verbose, remove=args.git_rm) |
| 200 | + |
| 201 | + dump(targets_file_name, targets) |
| 202 | + |
| 203 | + for process in git_processes : |
| 204 | + process.wait() |
0 commit comments