Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / build_tools / parse_dsc.py
1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 import collections
7 import fnmatch
8 import optparse
9 import os
10 import sys
11
12 VALID_TOOLCHAINS = [
13   'bionic',
14   'newlib',
15   'glibc',
16   'pnacl',
17   'win',
18   'linux',
19   'mac',
20 ]
21
22 # 'KEY' : ( <TYPE>, [Accepted Values], <Required?>)
23 DSC_FORMAT = {
24     'DISABLE': (bool, [True, False], False),
25     'SEL_LDR': (bool, [True, False], False),
26     # Disable this project from being included in the NaCl packaged app.
27     'DISABLE_PACKAGE': (bool, [True, False], False),
28     # Don't generate the additional files to allow this project to run as a
29     # packaged app (i.e. manifest.json, background.js, etc.).
30     'NO_PACKAGE_FILES': (bool, [True, False], False),
31     'TOOLS' : (list, VALID_TOOLCHAINS, True),
32     'CONFIGS' : (list, ['Debug', 'Release'], False),
33     'PREREQ' : (list, '', False),
34     'TARGETS' : (list, {
35         'NAME': (str, '', True),
36         # main = nexe target
37         # lib = library target
38         # so = shared object target, automatically added to NMF
39         # so-standalone =  shared object target, not put into NMF
40         'TYPE': (str, ['main', 'lib', 'static-lib', 'so', 'so-standalone'],
41                  True),
42         'SOURCES': (list, '', True),
43         'CFLAGS': (list, '', False),
44         'CFLAGS_GCC': (list, '', False),
45         'CXXFLAGS': (list, '', False),
46         'DEFINES': (list, '', False),
47         'LDFLAGS': (list, '', False),
48         'INCLUDES': (list, '', False),
49         'LIBS' : (dict, VALID_TOOLCHAINS, False),
50         'DEPS' : (list, '', False)
51     }, False),
52     'HEADERS': (list, {
53         'FILES': (list, '', True),
54         'DEST': (str, '', True),
55     }, False),
56     'SEARCH': (list, '', False),
57     'POST': (str, '', False),
58     'PRE': (str, '', False),
59     'DEST': (str, ['getting_started', 'examples/api', 'examples/benchmarks',
60                    'examples/demo', 'examples/tutorial',
61                    'src', 'tests'], True),
62     'NAME': (str, '', False),
63     'DATA': (list, '', False),
64     'TITLE': (str, '', False),
65     'GROUP': (str, '', False),
66     'EXPERIMENTAL': (bool, [True, False], False),
67     'PERMISSIONS': (list, '', False),
68     'SOCKET_PERMISSIONS': (list, '', False),
69     'FILESYSTEM_PERMISSIONS': (list, '', False),
70     'MULTI_PLATFORM': (bool, [True, False], False),
71     'MIN_CHROME_VERSION': (str, '', False),
72 }
73
74
75 class ValidationError(Exception):
76   pass
77
78
79 def ValidateFormat(src, dsc_format):
80   # Verify all required keys are there
81   for key in dsc_format:
82     exp_type, exp_value, required = dsc_format[key]
83     if required and key not in src:
84       raise ValidationError('Missing required key %s.' % key)
85
86   # For each provided key, verify it's valid
87   for key in src:
88     # Verify the key is known
89     if key not in dsc_format:
90       raise ValidationError('Unexpected key %s.' % key)
91
92     exp_type, exp_value, required = dsc_format[key]
93     value = src[key]
94
95     # Verify the value is non-empty if required
96     if required and not value:
97       raise ValidationError('Expected non-empty value for %s.' % key)
98
99     # If the expected type is a dict, but the provided type is a list
100     # then the list applies to all keys of the dictionary, so we reset
101     # the expected type and value.
102     if exp_type is dict:
103       if type(value) is list:
104         exp_type = list
105         exp_value = ''
106
107     # Verify the key is of the expected type
108     if exp_type != type(value):
109       raise ValidationError('Key %s expects %s not %s.' % (
110           key, exp_type.__name__.upper(), type(value).__name__.upper()))
111
112     # If it's a bool, the expected values are always True or False.
113     if exp_type is bool:
114       continue
115
116     # If it's a string and there are expected values, make sure it matches
117     if exp_type is str:
118       if type(exp_value) is list and exp_value:
119         if value not in exp_value:
120           raise ValidationError("Value '%s' not expected for %s." %
121                                 (value, key))
122       continue
123
124     # if it's a list, then we need to validate the values
125     if exp_type is list:
126       # If we expect a dictionary, then call this recursively
127       if type(exp_value) is dict:
128         for val in value:
129           ValidateFormat(val, exp_value)
130         continue
131       # If we expect a list of strings
132       if type(exp_value) is str:
133         for val in value:
134           if type(val) is not str:
135             raise ValidationError('Value %s in %s is not a string.' %
136                                   (val, key))
137         continue
138       # if we expect a particular string
139       if type(exp_value) is list:
140         for val in value:
141           if val not in exp_value:
142             raise ValidationError('Value %s not expected in %s.' %
143                                   (val, key))
144         continue
145
146     # if we are expecting a dict, verify the keys are allowed
147     if exp_type is dict:
148       print "Expecting dict\n"
149       for sub in value:
150         if sub not in exp_value:
151           raise ValidationError('Sub key %s not expected in %s.' %
152                                 (sub, key))
153       continue
154
155     # If we got this far, it's an unexpected type
156     raise ValidationError('Unexpected type %s for key %s.' %
157                           (str(type(src[key])), key))
158
159
160 def LoadProject(filename):
161   with open(filename, 'r') as descfile:
162     try:
163       desc = eval(descfile.read(), {}, {})
164     except Exception as e:
165       raise ValidationError(e)
166   if desc.get('DISABLE', False):
167     return None
168   ValidateFormat(desc, DSC_FORMAT)
169   desc['FILEPATH'] = os.path.abspath(filename)
170   return desc
171
172
173 def LoadProjectTreeUnfiltered(srcpath):
174   # Build the tree
175   out = collections.defaultdict(list)
176   for root, _, files in os.walk(srcpath):
177     for filename in files:
178       if fnmatch.fnmatch(filename, '*.dsc'):
179         filepath = os.path.join(root, filename)
180         try:
181           desc = LoadProject(filepath)
182         except ValidationError as e:
183           raise ValidationError("Failed to validate: %s: %s" % (filepath, e))
184         if desc:
185           key = desc['DEST']
186           out[key].append(desc)
187   return out
188
189
190 def LoadProjectTree(srcpath, include, exclude=None):
191   out = LoadProjectTreeUnfiltered(srcpath)
192   return FilterTree(out, MakeDefaultFilterFn(include, exclude))
193
194
195 def GenerateProjects(tree):
196   for key in tree:
197     for val in tree[key]:
198       yield key, val
199
200
201 def FilterTree(tree, filter_fn):
202   out = collections.defaultdict(list)
203   for branch, desc in GenerateProjects(tree):
204     if filter_fn(desc):
205       out[branch].append(desc)
206   return out
207
208
209 def MakeDefaultFilterFn(include, exclude):
210   def DefaultFilterFn(desc):
211     matches_include = not include or DescMatchesFilter(desc, include)
212     matches_exclude = exclude and DescMatchesFilter(desc, exclude)
213
214     # Exclude list overrides include list.
215     if matches_exclude:
216       return False
217     return matches_include
218
219   return DefaultFilterFn
220
221
222 def DescMatchesFilter(desc, filters):
223   for key, expected in filters.iteritems():
224     # For any filtered key which is unspecified, assumed False
225     value = desc.get(key, False)
226
227     # If we provide an expected list, match at least one
228     if type(expected) != list:
229       expected = set([expected])
230     if type(value) != list:
231       value = set([value])
232
233     if not set(expected) & set(value):
234       return False
235
236   # If we fall through, then we matched the filters
237   return True
238
239
240 def PrintProjectTree(tree):
241   for key in tree:
242     print key + ':'
243     for val in tree[key]:
244       print '\t' + val['NAME']
245
246
247 def main(argv):
248   parser = optparse.OptionParser(usage='%prog [options] <dir>')
249   parser.add_option('-e', '--experimental',
250       help='build experimental examples and libraries', action='store_true')
251   parser.add_option('-t', '--toolchain',
252       help='Build using toolchain. Can be passed more than once.',
253       action='append')
254
255   options, args = parser.parse_args(argv[1:])
256   filters = {}
257
258   load_from_dir = '.'
259   if len(args) > 1:
260     parser.error('Expected 0 or 1 args, got %d.' % len(args))
261
262   if args:
263     load_from_dir = args[0]
264
265   if options.toolchain:
266     filters['TOOLS'] = options.toolchain
267
268   if not options.experimental:
269     filters['EXPERIMENTAL'] = False
270
271   try:
272     tree = LoadProjectTree(load_from_dir, include=filters)
273   except ValidationError as e:
274     sys.stderr.write(str(e) + '\n')
275     return 1
276
277   PrintProjectTree(tree)
278   return 0
279
280
281 if __name__ == '__main__':
282   sys.exit(main(sys.argv))