1 # Copyright (C) 2013 Google Inc. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 # NOTE: This has only been used to parse
33 # core/page/RuntimeEnabledFeatures.in and may not be capable
34 # of parsing other .in files correctly.
38 # name1 arg=value, arg2=value2, arg2=value3
40 # InFile must be passed a dictionary of default values
41 # with which to validate arguments against known names.
42 # Sequence types as default values will produce sequences
44 # Bare arguments (no '=') are treated as names with value True.
45 # The first field will always be labeled 'name'.
47 # InFile.load_from_files(['file.in'], {'arg': None, 'arg2': []})
49 # Parsing produces an array of dictionaries:
50 # [ { 'name' : 'name1', 'arg' :' value', arg2=['value2', 'value3'] }
52 def _is_comment(line
):
53 return line
.startswith("//") or line
.startswith("#")
56 def __init__(self
, file_paths
, lines
, defaults
, valid_values
=None, default_parameters
=None):
57 self
.file_paths
= file_paths
58 self
.name_dictionaries
= []
59 self
.parameters
= copy
.deepcopy(default_parameters
if default_parameters
else {})
60 self
._defaults
= defaults
61 self
._valid
_values
= copy
.deepcopy(valid_values
if valid_values
else {})
62 self
._parse
(map(str.strip
, lines
))
65 def load_from_files(self
, file_paths
, defaults
, valid_values
, default_parameters
):
67 for path
in file_paths
:
68 assert path
.endswith(".in")
69 with
open(os
.path
.abspath(path
)) as in_file
:
70 lines
+= in_file
.readlines()
71 return InFile(file_paths
, lines
, defaults
, valid_values
, default_parameters
)
73 def _is_sequence(self
, arg
):
74 return (not hasattr(arg
, "strip")
75 and hasattr(arg
, "__getitem__")
76 or hasattr(arg
, "__iter__"))
78 def _parse(self
, lines
):
79 parsing_parameters
= True
85 parsing_parameters
= False
87 if parsing_parameters
:
88 self
._parse
_parameter
(line
)
90 entry
= self
._parse
_line
(line
)
93 entry
= self
._merge
_entries
(entry
, self
.name_dictionaries
[indices
[name
]])
95 self
.name_dictionaries
[indices
[name
]] = entry
97 indices
[name
] = len(self
.name_dictionaries
)
98 self
.name_dictionaries
.append(entry
)
101 def _merge_entries(self
, one
, two
):
105 self
._fatal
("Expected key '%s' not found in entry: %s" % (key
, two
))
106 if one
[key
] and two
[key
]:
109 if isinstance(val_one
, list) and isinstance(val_two
, list):
110 val
= val_one
+ val_two
111 elif isinstance(val_one
, list):
112 val
= val_one
+ [val_two
]
113 elif isinstance(val_two
, list):
114 val
= [val_one
] + val_two
116 val
= [val_one
, val_two
]
119 merged
[key
] = one
[key
]
121 merged
[key
] = two
[key
]
125 def _parse_parameter(self
, line
):
127 name
, value
= line
.split('=')
129 name
, value
= line
, True
130 if not name
in self
.parameters
:
131 self
._fatal
("Unknown parameter: '%s' in line:\n%s\nKnown parameters: %s" % (name
, line
, self
.parameters
.keys()))
132 self
.parameters
[name
] = value
134 def _parse_line(self
, line
):
135 args
= copy
.deepcopy(self
._defaults
)
136 parts
= line
.split(' ')
137 args
['name'] = parts
[0]
138 # re-join the rest of the line and split on ','
139 args_list
= ' '.join(parts
[1:]).strip().split(',')
140 for arg_string
in args_list
:
141 arg_string
= arg_string
.strip()
142 if not arg_string
: # Ignore empty args
144 if '=' in arg_string
:
145 arg_name
, arg_value
= arg_string
.split('=')
147 arg_name
, arg_value
= arg_string
, True
148 if arg_name
not in self
._defaults
:
149 self
._fatal
("Unknown argument: '%s' in line:\n%s\nKnown arguments: %s" % (arg_name
, line
, self
._defaults
.keys()))
150 valid_values
= self
._valid
_values
.get(arg_name
)
151 if valid_values
and arg_value
not in valid_values
:
152 self
._fatal
("Unknown value: '%s' in line:\n%s\nKnown values: %s" % (arg_value
, line
, valid_values
))
153 if self
._is
_sequence
(args
[arg_name
]):
154 args
[arg_name
].append(arg_value
)
156 args
[arg_name
] = arg_value
159 def _fatal(self
, message
):
160 # FIXME: This should probably raise instead of exit(1)