Upstream version 7.36.149.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',
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     'MULTI_PLATFORM': (bool, [True, False], False),
70 }
71
72
73 class ValidationError(Exception):
74   pass
75
76
77 def ValidateFormat(src, dsc_format):
78   # Verify all required keys are there
79   for key in dsc_format:
80     exp_type, exp_value, required = dsc_format[key]
81     if required and key not in src:
82       raise ValidationError('Missing required key %s.' % key)
83
84   # For each provided key, verify it's valid
85   for key in src:
86     # Verify the key is known
87     if key not in dsc_format:
88       raise ValidationError('Unexpected key %s.' % key)
89
90     exp_type, exp_value, required = dsc_format[key]
91     value = src[key]
92
93     # Verify the value is non-empty if required
94     if required and not value:
95       raise ValidationError('Expected non-empty value for %s.' % key)
96
97     # If the expected type is a dict, but the provided type is a list
98     # then the list applies to all keys of the dictionary, so we reset
99     # the expected type and value.
100     if exp_type is dict:
101       if type(value) is list:
102         exp_type = list
103         exp_value = ''
104
105     # Verify the key is of the expected type
106     if exp_type != type(value):
107       raise ValidationError('Key %s expects %s not %s.' % (
108           key, exp_type.__name__.upper(), type(value).__name__.upper()))
109
110     # If it's a bool, the expected values are always True or False.
111     if exp_type is bool:
112       continue
113
114     # If it's a string and there are expected values, make sure it matches
115     if exp_type is str:
116       if type(exp_value) is list and exp_value:
117         if value not in exp_value:
118           raise ValidationError("Value '%s' not expected for %s." %
119                                 (value, key))
120       continue
121
122     # if it's a list, then we need to validate the values
123     if exp_type is list:
124       # If we expect a dictionary, then call this recursively
125       if type(exp_value) is dict:
126         for val in value:
127           ValidateFormat(val, exp_value)
128         continue
129       # If we expect a list of strings
130       if type(exp_value) is str:
131         for val in value:
132           if type(val) is not str:
133             raise ValidationError('Value %s in %s is not a string.' %
134                                   (val, key))
135         continue
136       # if we expect a particular string
137       if type(exp_value) is list:
138         for val in value:
139           if val not in exp_value:
140             raise ValidationError('Value %s not expected in %s.' %
141                                   (val, key))
142         continue
143
144     # if we are expecting a dict, verify the keys are allowed
145     if exp_type is dict:
146       print "Expecting dict\n"
147       for sub in value:
148         if sub not in exp_value:
149           raise ValidationError('Sub key %s not expected in %s.' %
150                                 (sub, key))
151       continue
152
153     # If we got this far, it's an unexpected type
154     raise ValidationError('Unexpected type %s for key %s.' %
155                           (str(type(src[key])), key))
156
157
158 def LoadProject(filename):
159   with open(filename, 'r') as descfile:
160     try:
161       desc = eval(descfile.read(), {}, {})
162     except Exception as e:
163       raise ValidationError(e)
164   if desc.get('DISABLE', False):
165     return None
166   ValidateFormat(desc, DSC_FORMAT)
167   desc['FILEPATH'] = os.path.abspath(filename)
168   return desc
169
170
171 def LoadProjectTreeUnfiltered(srcpath):
172   # Build the tree
173   out = collections.defaultdict(list)
174   for root, _, files in os.walk(srcpath):
175     for filename in files:
176       if fnmatch.fnmatch(filename, '*.dsc'):
177         filepath = os.path.join(root, filename)
178         try:
179           desc = LoadProject(filepath)
180         except ValidationError as e:
181           raise ValidationError("Failed to validate: %s: %s" % (filepath, e))
182         if desc:
183           key = desc['DEST']
184           out[key].append(desc)
185   return out
186
187
188 def LoadProjectTree(srcpath, include, exclude=None):
189   out = LoadProjectTreeUnfiltered(srcpath)
190   return FilterTree(out, MakeDefaultFilterFn(include, exclude))
191
192
193 def GenerateProjects(tree):
194   for key in tree:
195     for val in tree[key]:
196       yield key, val
197
198
199 def FilterTree(tree, filter_fn):
200   out = collections.defaultdict(list)
201   for branch, desc in GenerateProjects(tree):
202     if filter_fn(desc):
203       out[branch].append(desc)
204   return out
205
206
207 def MakeDefaultFilterFn(include, exclude):
208   def DefaultFilterFn(desc):
209     matches_include = not include or DescMatchesFilter(desc, include)
210     matches_exclude = exclude and DescMatchesFilter(desc, exclude)
211
212     # Exclude list overrides include list.
213     if matches_exclude:
214       return False
215     return matches_include
216
217   return DefaultFilterFn
218
219
220 def DescMatchesFilter(desc, filters):
221   for key, expected in filters.iteritems():
222     # For any filtered key which is unspecified, assumed False
223     value = desc.get(key, False)
224
225     # If we provide an expected list, match at least one
226     if type(expected) != list:
227       expected = set([expected])
228     if type(value) != list:
229       value = set([value])
230
231     if not set(expected) & set(value):
232       return False
233
234   # If we fall through, then we matched the filters
235   return True
236
237
238 def PrintProjectTree(tree):
239   for key in tree:
240     print key + ':'
241     for val in tree[key]:
242       print '\t' + val['NAME']
243
244
245 def main(argv):
246   parser = optparse.OptionParser(usage='%prog [options] <dir>')
247   parser.add_option('-e', '--experimental',
248       help='build experimental examples and libraries', action='store_true')
249   parser.add_option('-t', '--toolchain',
250       help='Build using toolchain. Can be passed more than once.',
251       action='append')
252
253   options, args = parser.parse_args(argv[1:])
254   filters = {}
255
256   load_from_dir = '.'
257   if len(args) > 1:
258     parser.error('Expected 0 or 1 args, got %d.' % len(args))
259
260   if args:
261     load_from_dir = args[0]
262
263   if options.toolchain:
264     filters['TOOLS'] = options.toolchain
265
266   if not options.experimental:
267     filters['EXPERIMENTAL'] = False
268
269   try:
270     tree = LoadProjectTree(load_from_dir, include=filters)
271   except ValidationError as e:
272     sys.stderr.write(str(e) + '\n')
273     return 1
274
275   PrintProjectTree(tree)
276   return 0
277
278
279 if __name__ == '__main__':
280   sys.exit(main(sys.argv))