Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(58)

Side by Side Diff: components/policy/tools/generate_policy_source.py

Issue 228423002: Add $ref support to policy schema (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@policy-schema-regex
Patch Set: fixes Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/policy/resources/policy_templates.json ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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())
OLDNEW
« no previous file with comments | « components/policy/resources/policy_templates.json ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698