153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# Copyright (C) 2013 Google Inc. All rights reserved. 253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# 353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# Redistribution and use in source and binary forms, with or without 453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# modification, are permitted provided that the following conditions are 553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# met: 653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# 753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# * Redistributions of source code must retain the above copyright 853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# notice, this list of conditions and the following disclaimer. 953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# * Redistributions in binary form must reproduce the above 1053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# copyright notice, this list of conditions and the following disclaimer 1153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# in the documentation and/or other materials provided with the 1253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# distribution. 1353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# * Neither the name of Google Inc. nor the names of its 1453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# contributors may be used to endorse or promote products derived from 1553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# this software without specific prior written permission. 1653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# 1753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 2953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)import copy 3053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)import os 3153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 3253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# NOTE: This has only been used to parse 3353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# core/page/RuntimeEnabledFeatures.in and may not be capable 3453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# of parsing other .in files correctly. 3553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 3653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# .in file format is: 3753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# // comment 3853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# name1 arg=value, arg2=value2, arg2=value3 3953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# 4053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# InFile must be passed a dictionary of default values 4153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# with which to validate arguments against known names. 4253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# Sequence types as default values will produce sequences 4353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# as parse results. 4453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# Bare arguments (no '=') are treated as names with value True. 4553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# The first field will always be labeled 'name'. 4653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# 4793ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles)# InFile.load_from_files(['file.in'], {'arg': None, 'arg2': []}) 4853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# 4953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# Parsing produces an array of dictionaries: 5053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)# [ { 'name' : 'name1', 'arg' :' value', arg2=['value2', 'value3'] } 5153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 5253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)def _is_comment(line): 5353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) return line.startswith("//") or line.startswith("#") 5453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 5553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)class InFile(object): 5681a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles) def __init__(self, lines, defaults, valid_values=None, default_parameters=None): 5753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) self.name_dictionaries = [] 5853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) self.parameters = copy.deepcopy(default_parameters if default_parameters else {}) 5953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) self._defaults = defaults 6081a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles) self._valid_values = copy.deepcopy(valid_values if valid_values else {}) 6153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) self._parse(map(str.strip, lines)) 6253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 6353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) @classmethod 6493ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles) def load_from_files(self, file_paths, defaults, valid_values, default_parameters): 6593ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles) lines = [] 6693ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles) for path in file_paths: 67197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch assert path.endswith(".in") 6893ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles) with open(os.path.abspath(path)) as in_file: 6993ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles) lines += in_file.readlines() 7093ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles) return InFile(lines, defaults, valid_values, default_parameters) 7153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 7253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) def _is_sequence(self, arg): 7353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) return (not hasattr(arg, "strip") 7453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) and hasattr(arg, "__getitem__") 7553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) or hasattr(arg, "__iter__")) 7653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 7753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) def _parse(self, lines): 7853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) parsing_parameters = True 79591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch indices = {} 8053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) for line in lines: 8153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if _is_comment(line): 8253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) continue 8353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if not line: 8453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) parsing_parameters = False 8553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) continue 8653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if parsing_parameters: 8753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) self._parse_parameter(line) 8853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) else: 89591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch entry = self._parse_line(line) 90591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch name = entry['name'] 91591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch if name in indices: 92591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch entry = self._merge_entries(entry, self.name_dictionaries[indices[name]]) 93591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch entry['name'] = name 94591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch self.name_dictionaries[indices[name]] = entry 95591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch else: 96591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch indices[name] = len(self.name_dictionaries) 97591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch self.name_dictionaries.append(entry) 98591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 99591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 100591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch def _merge_entries(self, one, two): 101591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch merged = {} 102591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch for key in one: 103591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch if key not in two: 104591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch self._fatal("Expected key '%s' not found in entry: %s" % (key, two)) 105591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch if one[key] and two[key]: 106591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch val_one = one[key] 107591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch val_two = two[key] 108591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch if isinstance(val_one, list) and isinstance(val_two, list): 109591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch val = val_one + val_two 110591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch elif isinstance(val_one, list): 111591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch val = val_one + [val_two] 112591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch elif isinstance(val_two, list): 113591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch val = [val_one] + val_two 114591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch else: 115591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch val = [val_one, val_two] 116591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch merged[key] = val 117591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch elif one[key]: 118591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch merged[key] = one[key] 119591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch else: 120591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch merged[key] = two[key] 121591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch return merged 122591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch 12353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 12453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) def _parse_parameter(self, line): 12553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if '=' in line: 12653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) name, value = line.split('=') 12753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) else: 12853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) name, value = line, True 12953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if not name in self.parameters: 13053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) self._fatal("Unknown parameter: '%s' in line:\n%s\nKnown parameters: %s" % (name, line, self.parameters.keys())) 13153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) self.parameters[name] = value 13253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 13353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) def _parse_line(self, line): 13453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) args = copy.deepcopy(self._defaults) 13553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) parts = line.split(' ') 13653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) args['name'] = parts[0] 13753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # re-join the rest of the line and split on ',' 13853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) args_list = ' '.join(parts[1:]).strip().split(',') 13953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) for arg_string in args_list: 14053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) arg_string = arg_string.strip() 14153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if not arg_string: # Ignore empty args 14253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) continue 14353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if '=' in arg_string: 14453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) arg_name, arg_value = arg_string.split('=') 14553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) else: 14653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) arg_name, arg_value = arg_string, True 14753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if arg_name not in self._defaults: 14853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) self._fatal("Unknown argument: '%s' in line:\n%s\nKnown arguments: %s" % (arg_name, line, self._defaults.keys())) 14981a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles) valid_values = self._valid_values.get(arg_name) 15081a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles) if valid_values and arg_value not in valid_values: 15181a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles) self._fatal("Unknown value: '%s' in line:\n%s\nKnown values: %s" % (arg_value, line, valid_values)) 15253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if self._is_sequence(args[arg_name]): 15353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) args[arg_name].append(arg_value) 15453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) else: 15553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) args[arg_name] = arg_value 15653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) return args 15753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 15853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) def _fatal(self, message): 15953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # FIXME: This should probably raise instead of exit(1) 16053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) print message 16153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) exit(1) 162