OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 '''python %prog [options] platform chromium_os_flag template | 6 '''python %prog [options] platform chromium_os_flag template |
7 | 7 |
8 platform specifies which platform source is being generated for | 8 platform specifies which platform source is being generated for |
9 and can be one of (win, mac, linux) | 9 and can be one of (win, mac, linux) |
10 chromium_os_flag should be 1 if this is a Chromium OS build | 10 chromium_os_flag should be 1 if this is a Chromium OS build |
11 template is the path to a .json policy template file.''' | 11 template is the path to a .json policy template file.''' |
12 | 12 |
13 from __future__ import with_statement | 13 from __future__ import with_statement |
14 from functools import partial | |
14 import json | 15 import json |
15 from optparse import OptionParser | 16 from optparse import OptionParser |
16 import re | 17 import re |
17 import sys | 18 import sys |
18 import textwrap | 19 import textwrap |
20 import types | |
19 | 21 |
20 | 22 |
21 CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome' | 23 CHROME_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Google\\\\Chrome' |
22 CHROMIUM_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Chromium' | 24 CHROMIUM_POLICY_KEY = 'SOFTWARE\\\\Policies\\\\Chromium' |
23 | 25 |
24 | 26 |
25 class PolicyDetails: | 27 class PolicyDetails: |
26 """Parses a policy template and caches all its details.""" | 28 """Parses a policy template and caches all its details.""" |
27 | 29 |
28 # Maps policy types to a tuple with 3 other types: | 30 # Maps policy types to a tuple with 3 other types: |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
275 self.string_enums = [] | 277 self.string_enums = [] |
276 self.simple_types = { | 278 self.simple_types = { |
277 'boolean': None, | 279 'boolean': None, |
278 'integer': None, | 280 'integer': None, |
279 'null': None, | 281 'null': None, |
280 'number': None, | 282 'number': None, |
281 'string': None, | 283 'string': None, |
282 } | 284 } |
283 self.stringlist_type = None | 285 self.stringlist_type = None |
284 self.ranges = {} | 286 self.ranges = {} |
287 self.id_map = {} | |
285 | 288 |
286 def GetString(self, s): | 289 def GetString(self, s): |
287 if s in self.shared_strings: | 290 if s in self.shared_strings: |
288 return self.shared_strings[s] | 291 return self.shared_strings[s] |
289 # Generate JSON escaped string, which is slightly different from desired | 292 # Generate JSON escaped string, which is slightly different from desired |
290 # C/C++ escaped string. Known differences includes unicode escaping format. | 293 # C/C++ escaped string. Known differences includes unicode escaping format. |
291 return json.dumps(s) | 294 return json.dumps(s) |
292 | 295 |
293 def AppendSchema(self, type, extra, comment=''): | 296 def AppendSchema(self, type, extra, comment=''): |
294 index = len(self.schema_nodes) | 297 index = len(self.schema_nodes) |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
391 str(min_value) if min_value_set else 'INT_MIN') | 394 str(min_value) if min_value_set else 'INT_MIN') |
392 return self.AppendSchema('TYPE_INTEGER', | 395 return self.AppendSchema('TYPE_INTEGER', |
393 index, | 396 index, |
394 'integer with ranged restriction: %s' % name) | 397 'integer with ranged restriction: %s' % name) |
395 | 398 |
396 def Generate(self, schema, name): | 399 def Generate(self, schema, name): |
397 """Generates the structs for the given schema. | 400 """Generates the structs for the given schema. |
398 | 401 |
399 |schema|: a valid JSON schema in a dictionary. | 402 |schema|: a valid JSON schema in a dictionary. |
400 |name|: the name of the current node, for the generated comments.""" | 403 |name|: the name of the current node, for the generated comments.""" |
404 if schema.has_key('$ref'): | |
405 if schema.has_key('id'): | |
406 raise RuntimeError("Schemas with a $ref can't have an id") | |
407 if not isinstance(schema['$ref'], types.StringTypes): | |
408 raise RuntimeError("$ref attribute must be a string") | |
409 return schema['$ref'] | |
401 if schema['type'] in self.simple_types: | 410 if schema['type'] in self.simple_types: |
402 if not self.SchemaHaveRestriction(schema): | 411 if not self.SchemaHaveRestriction(schema): |
403 # Simple types use shared nodes. | 412 # Simple types use shared nodes. |
404 return self.GetSimpleType(schema['type']) | 413 return self.GetSimpleType(schema['type']) |
405 elif 'enum' in schema: | 414 elif 'enum' in schema: |
406 return self.GetEnumType(schema, name) | 415 return self.GetEnumType(schema, name) |
407 elif 'pattern' in schema: | 416 elif 'pattern' in schema: |
408 return self.GetPatternType(schema, name) | 417 return self.GetPatternType(schema, name) |
409 else: | 418 else: |
410 return self.GetRangedType(schema, name) | 419 return self.GetRangedType(schema, name) |
411 | 420 |
412 if schema['type'] == 'array': | 421 if schema['type'] == 'array': |
413 # Special case for lists of strings, which is a common policy type. | 422 # Special case for lists of strings, which is a common policy type. |
414 if schema['items']['type'] == 'string': | 423 if schema['items']['type'] == 'string': |
415 return self.GetStringList() | 424 return self.GetStringList() |
416 return self.AppendSchema( | 425 return self.AppendSchema('TYPE_LIST', |
417 'TYPE_LIST', | 426 self.GenerateAndCollectID(schema['items'], 'items of ' + name)) |
418 self.Generate(schema['items'], 'items of ' + name)) | |
419 elif schema['type'] == 'object': | 427 elif schema['type'] == 'object': |
420 # Reserve an index first, so that dictionaries come before their | 428 # Reserve an index first, so that dictionaries come before their |
421 # properties. This makes sure that the root node is the first in the | 429 # properties. This makes sure that the root node is the first in the |
422 # SchemaNodes array. | 430 # SchemaNodes array. |
423 index = self.AppendSchema('TYPE_DICTIONARY', -1) | 431 index = self.AppendSchema('TYPE_DICTIONARY', -1) |
424 | 432 |
425 if 'additionalProperties' in schema: | 433 if 'additionalProperties' in schema: |
426 additionalProperties = self.Generate( | 434 additionalProperties = self.GenerateAndCollectID( |
427 schema['additionalProperties'], | 435 schema['additionalProperties'], |
428 'additionalProperties of ' + name) | 436 'additionalProperties of ' + name) |
429 else: | 437 else: |
430 additionalProperties = -1 | 438 additionalProperties = -1 |
431 | 439 |
432 # Properties must be sorted by name, for the binary search lookup. | 440 # Properties must be sorted by name, for the binary search lookup. |
433 # Note that |properties| must be evaluated immediately, so that all the | 441 # Note that |properties| must be evaluated immediately, so that all the |
434 # recursive calls to Generate() append the necessary child nodes; if | 442 # recursive calls to Generate() append the necessary child nodes; if |
435 # |properties| were a generator then this wouldn't work. | 443 # |properties| were a generator then this wouldn't work. |
436 sorted_properties = sorted(schema.get('properties', {}).items()) | 444 sorted_properties = sorted(schema.get('properties', {}).items()) |
437 properties = [ (self.GetString(key), self.Generate(subschema, key)) | 445 properties = [ (self.GetString(key), |
438 for key, subschema in sorted_properties ] | 446 self.GenerateAndCollectID(subschema, key)) |
Joao da Silva
2014/04/09 12:34:55
I think it's more readable if the tuple is all in
binjin
2014/04/09 14:09:59
Done.
| |
447 for key, subschema in sorted_properties ] | |
439 | 448 |
440 pattern_properties = [] | 449 pattern_properties = [] |
441 for pattern, subschema in schema.get('patternProperties', {}).items(): | 450 for pattern, subschema in schema.get('patternProperties', {}).items(): |
442 pattern_properties.append((self.GetString(pattern), | 451 pattern_properties.append((self.GetString(pattern), |
443 self.Generate(subschema, pattern))); | 452 self.GenerateAndCollectID(subschema, pattern))); |
444 | 453 |
445 begin = len(self.property_nodes) | 454 begin = len(self.property_nodes) |
446 self.property_nodes += properties | 455 self.property_nodes += properties |
447 end = len(self.property_nodes) | 456 end = len(self.property_nodes) |
448 self.property_nodes += pattern_properties | 457 self.property_nodes += pattern_properties |
449 pattern_end = len(self.property_nodes) | 458 pattern_end = len(self.property_nodes) |
450 | 459 |
451 if index == 0: | 460 if index == 0: |
452 self.root_properties_begin = begin | 461 self.root_properties_begin = begin |
453 self.root_properties_end = end | 462 self.root_properties_end = end |
454 | 463 |
455 extra = len(self.properties_nodes) | 464 extra = len(self.properties_nodes) |
456 self.properties_nodes.append((begin, end, pattern_end, | 465 self.properties_nodes.append((begin, end, pattern_end, |
457 additionalProperties, name)) | 466 additionalProperties, name)) |
458 | 467 |
459 # Set the right data at |index| now. | 468 # Set the right data at |index| now. |
460 self.schema_nodes[index] = ('TYPE_DICTIONARY', extra, name) | 469 self.schema_nodes[index] = ('TYPE_DICTIONARY', extra, name) |
461 return index | 470 return index |
462 else: | 471 else: |
463 assert False | 472 assert False |
464 | 473 |
474 def GenerateAndCollectID(self, schema, name): | |
475 """A wrapper of Generate(), will take the return value, check and add 'id' | |
476 attribute to self.id_map. This wrapper need to be used for every call to | |
Joao da Silva
2014/04/09 12:34:55
This wrapper needs
binjin
2014/04/09 14:09:59
Done.
| |
477 Generate(). | |
478 """ | |
479 index = self.Generate(schema, name) | |
480 if not schema.has_key('id'): | |
481 return index | |
482 id_str = schema['id'] | |
483 if self.id_map.has_key(id_str): | |
484 raise RuntimeError('Duplicated id: ' + id_str) | |
485 self.id_map[id_str] = index | |
486 return index | |
487 | |
465 def Write(self, f): | 488 def Write(self, f): |
466 """Writes the generated structs to the given file. | 489 """Writes the generated structs to the given file. |
467 | 490 |
468 |f| an open file to write to.""" | 491 |f| an open file to write to.""" |
469 f.write('const internal::SchemaNode kSchemas[] = {\n' | 492 f.write('const internal::SchemaNode kSchemas[] = {\n' |
470 '// Type Extra\n') | 493 '// Type Extra\n') |
471 for type, extra, comment in self.schema_nodes: | 494 for type, extra, comment in self.schema_nodes: |
472 type += ',' | 495 type += ',' |
473 f.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra, comment)) | 496 f.write(' { base::Value::%-18s %3d }, // %s\n' % (type, extra, comment)) |
474 f.write('};\n\n') | 497 f.write('};\n\n') |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
509 | 532 |
510 f.write('const internal::SchemaData kChromeSchemaData = {\n' | 533 f.write('const internal::SchemaData kChromeSchemaData = {\n' |
511 ' kSchemas,\n') | 534 ' kSchemas,\n') |
512 f.write(' kPropertyNodes,\n' if self.property_nodes else ' NULL,\n') | 535 f.write(' kPropertyNodes,\n' if self.property_nodes else ' NULL,\n') |
513 f.write(' kProperties,\n' if self.properties_nodes else ' NULL,\n') | 536 f.write(' kProperties,\n' if self.properties_nodes else ' NULL,\n') |
514 f.write(' kRestrictionNodes,\n' if self.restriction_nodes else ' NULL,\n') | 537 f.write(' kRestrictionNodes,\n' if self.restriction_nodes else ' NULL,\n') |
515 f.write(' kIntegerEnumerations,\n' if self.int_enums else ' NULL,\n') | 538 f.write(' kIntegerEnumerations,\n' if self.int_enums else ' NULL,\n') |
516 f.write(' kStringEnumerations,\n' if self.string_enums else ' NULL,\n') | 539 f.write(' kStringEnumerations,\n' if self.string_enums else ' NULL,\n') |
517 f.write('};\n\n') | 540 f.write('};\n\n') |
518 | 541 |
542 def GetByID(self, id_str): | |
543 if not isinstance(id_str, types.StringTypes): | |
544 return id_str | |
545 if not self.id_map.has_key(id_str): | |
546 raise RuntimeError('Invalid $ref: ' + id_str) | |
547 return self.id_map[id_str] | |
548 | |
549 def ResolveID(self, index, params): | |
550 lst = list(params) | |
551 lst[index] = self.GetByID(lst[index]) | |
552 return tuple(lst) | |
Joao da Silva
2014/04/09 12:34:55
Suggestion:
def ResolveID(self, index, params):
binjin
2014/04/09 14:09:59
Done.
| |
553 | |
554 def ResolveReferences(self): | |
555 """Resolve reference mapping, required to be called after Generate() | |
556 | |
557 After calling Generate(), the type of indices used in schema structures | |
558 might be either int or string. An int type suggest that it's a resolved | |
Joao da Silva
2014/04/09 12:34:55
suggests
binjin
2014/04/09 14:09:59
Done.
| |
559 index, but for string type it's unresolved. Resolving a reference is as | |
560 simple as looking up for corresponding ID in self.id_map, and replace the | |
561 old index with the mapped index. | |
562 """ | |
563 self.schema_nodes = map(partial(self.ResolveID, 1), self.schema_nodes) | |
564 self.property_nodes = map(partial(self.ResolveID, 1), self.property_nodes) | |
565 self.properties_nodes = map(partial(self.ResolveID, 3), | |
566 self.properties_nodes) | |
519 | 567 |
520 def _WritePolicyConstantSource(policies, os, f): | 568 def _WritePolicyConstantSource(policies, os, f): |
521 f.write('#include "policy/policy_constants.h"\n' | 569 f.write('#include "policy/policy_constants.h"\n' |
522 '\n' | 570 '\n' |
523 '#include <algorithm>\n' | 571 '#include <algorithm>\n' |
524 '#include <climits>\n' | 572 '#include <climits>\n' |
525 '\n' | 573 '\n' |
526 '#include "base/logging.h"\n' | 574 '#include "base/logging.h"\n' |
527 '#include "components/policy/core/common/schema_internal.h"\n' | 575 '#include "components/policy/core/common/schema_internal.h"\n' |
528 '\n' | 576 '\n' |
(...skipping 22 matching lines...) Expand all Loading... | |
551 for policy in policies: | 599 for policy in policies: |
552 if policy.is_supported: | 600 if policy.is_supported: |
553 f.write(' { %-14s %-16s %3s, %24s },\n' % ( | 601 f.write(' { %-14s %-16s %3s, %24s },\n' % ( |
554 'true,' if policy.is_deprecated else 'false,', | 602 'true,' if policy.is_deprecated else 'false,', |
555 'true,' if policy.is_device_only else 'false,', | 603 'true,' if policy.is_device_only else 'false,', |
556 policy.id, | 604 policy.id, |
557 policy.max_size)) | 605 policy.max_size)) |
558 f.write('};\n\n') | 606 f.write('};\n\n') |
559 | 607 |
560 schema_generator = SchemaNodesGenerator(shared_strings) | 608 schema_generator = SchemaNodesGenerator(shared_strings) |
561 schema_generator.Generate(chrome_schema, 'root node') | 609 schema_generator.GenerateAndCollectID(chrome_schema, 'root node') |
610 schema_generator.ResolveReferences() | |
562 schema_generator.Write(f) | 611 schema_generator.Write(f) |
563 | 612 |
564 f.write('bool CompareKeys(const internal::PropertyNode& node,\n' | 613 f.write('bool CompareKeys(const internal::PropertyNode& node,\n' |
565 ' const std::string& key) {\n' | 614 ' const std::string& key) {\n' |
566 ' return node.key < key;\n' | 615 ' return node.key < key;\n' |
567 '}\n\n') | 616 '}\n\n') |
568 | 617 |
569 f.write('} // namespace\n\n') | 618 f.write('} // namespace\n\n') |
570 | 619 |
571 if os == 'win': | 620 if os == 'win': |
(...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
870 def _WriteCloudPolicyDecoder(policies, os, f): | 919 def _WriteCloudPolicyDecoder(policies, os, f): |
871 f.write(CPP_HEAD) | 920 f.write(CPP_HEAD) |
872 for policy in policies: | 921 for policy in policies: |
873 if policy.is_supported and not policy.is_device_only: | 922 if policy.is_supported and not policy.is_device_only: |
874 _WritePolicyCode(f, policy) | 923 _WritePolicyCode(f, policy) |
875 f.write(CPP_FOOT) | 924 f.write(CPP_FOOT) |
876 | 925 |
877 | 926 |
878 if __name__ == '__main__': | 927 if __name__ == '__main__': |
879 sys.exit(main()) | 928 sys.exit(main()) |
OLD | NEW |