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 = [ |
438 for key, subschema in sorted_properties ] | 446 (self.GetString(key), self.GenerateAndCollectID(subschema, key)) |
| 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. The wrapper needs to be used for every call to |
| 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 return params[:index] + (self.GetByID(params[index]),) + params[index+1:] |
| 551 |
| 552 def ResolveReferences(self): |
| 553 """Resolve reference mapping, required to be called after Generate() |
| 554 |
| 555 After calling Generate(), the type of indices used in schema structures |
| 556 might be either int or string. An int type suggests that it's a resolved |
| 557 index, but for string type it's unresolved. Resolving a reference is as |
| 558 simple as looking up for corresponding ID in self.id_map, and replace the |
| 559 old index with the mapped index. |
| 560 """ |
| 561 self.schema_nodes = map(partial(self.ResolveID, 1), self.schema_nodes) |
| 562 self.property_nodes = map(partial(self.ResolveID, 1), self.property_nodes) |
| 563 self.properties_nodes = map(partial(self.ResolveID, 3), |
| 564 self.properties_nodes) |
519 | 565 |
520 def _WritePolicyConstantSource(policies, os, f): | 566 def _WritePolicyConstantSource(policies, os, f): |
521 f.write('#include "policy/policy_constants.h"\n' | 567 f.write('#include "policy/policy_constants.h"\n' |
522 '\n' | 568 '\n' |
523 '#include <algorithm>\n' | 569 '#include <algorithm>\n' |
524 '#include <climits>\n' | 570 '#include <climits>\n' |
525 '\n' | 571 '\n' |
526 '#include "base/logging.h"\n' | 572 '#include "base/logging.h"\n' |
527 '#include "components/policy/core/common/schema_internal.h"\n' | 573 '#include "components/policy/core/common/schema_internal.h"\n' |
528 '\n' | 574 '\n' |
(...skipping 22 matching lines...) Expand all Loading... |
551 for policy in policies: | 597 for policy in policies: |
552 if policy.is_supported: | 598 if policy.is_supported: |
553 f.write(' { %-14s %-16s %3s, %24s },\n' % ( | 599 f.write(' { %-14s %-16s %3s, %24s },\n' % ( |
554 'true,' if policy.is_deprecated else 'false,', | 600 'true,' if policy.is_deprecated else 'false,', |
555 'true,' if policy.is_device_only else 'false,', | 601 'true,' if policy.is_device_only else 'false,', |
556 policy.id, | 602 policy.id, |
557 policy.max_size)) | 603 policy.max_size)) |
558 f.write('};\n\n') | 604 f.write('};\n\n') |
559 | 605 |
560 schema_generator = SchemaNodesGenerator(shared_strings) | 606 schema_generator = SchemaNodesGenerator(shared_strings) |
561 schema_generator.Generate(chrome_schema, 'root node') | 607 schema_generator.GenerateAndCollectID(chrome_schema, 'root node') |
| 608 schema_generator.ResolveReferences() |
562 schema_generator.Write(f) | 609 schema_generator.Write(f) |
563 | 610 |
564 f.write('bool CompareKeys(const internal::PropertyNode& node,\n' | 611 f.write('bool CompareKeys(const internal::PropertyNode& node,\n' |
565 ' const std::string& key) {\n' | 612 ' const std::string& key) {\n' |
566 ' return node.key < key;\n' | 613 ' return node.key < key;\n' |
567 '}\n\n') | 614 '}\n\n') |
568 | 615 |
569 f.write('} // namespace\n\n') | 616 f.write('} // namespace\n\n') |
570 | 617 |
571 if os == 'win': | 618 if os == 'win': |
(...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
870 def _WriteCloudPolicyDecoder(policies, os, f): | 917 def _WriteCloudPolicyDecoder(policies, os, f): |
871 f.write(CPP_HEAD) | 918 f.write(CPP_HEAD) |
872 for policy in policies: | 919 for policy in policies: |
873 if policy.is_supported and not policy.is_device_only: | 920 if policy.is_supported and not policy.is_device_only: |
874 _WritePolicyCode(f, policy) | 921 _WritePolicyCode(f, policy) |
875 f.write(CPP_FOOT) | 922 f.write(CPP_FOOT) |
876 | 923 |
877 | 924 |
878 if __name__ == '__main__': | 925 if __name__ == '__main__': |
879 sys.exit(main()) | 926 sys.exit(main()) |
OLD | NEW |