Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2012 Google Inc. All rights reserved. | 1 # Copyright (c) 2012 Google Inc. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Xcode project file generator. | 5 """Xcode project file generator. |
| 6 | 6 |
| 7 This module is both an Xcode project file generator and a documentation of the | 7 This module is both an Xcode project file generator and a documentation of the |
| 8 Xcode project file format. Knowledge of the project file format was gained | 8 Xcode project file format. Knowledge of the project file format was gained |
| 9 based on extensive experience with Xcode, and by making changes to projects in | 9 based on extensive experience with Xcode, and by making changes to projects in |
| 10 Xcode.app and observing the resultant changes in the associated project files. | 10 Xcode.app and observing the resultant changes in the associated project files. |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 166 # Strings that match this pattern are quoted regardless of what _unquoted says. | 166 # Strings that match this pattern are quoted regardless of what _unquoted says. |
| 167 # Oddly, Xcode will quote any string with a run of three or more underscores. | 167 # Oddly, Xcode will quote any string with a run of three or more underscores. |
| 168 _quoted = re.compile('___') | 168 _quoted = re.compile('___') |
| 169 | 169 |
| 170 # This pattern should match any character that needs to be escaped by | 170 # This pattern should match any character that needs to be escaped by |
| 171 # XCObject._EncodeString. See that function. | 171 # XCObject._EncodeString. See that function. |
| 172 _escaped = re.compile('[\\\\"]|[\x00-\x1f]') | 172 _escaped = re.compile('[\\\\"]|[\x00-\x1f]') |
| 173 | 173 |
| 174 | 174 |
| 175 # Used by SourceTreeAndPathFromPath | 175 # Used by SourceTreeAndPathFromPath |
| 176 _path_leading_variable = re.compile('^\$\((.*?)\)(/(.*))?$') | 176 _path_leading_variable = re.compile(r'^\$\((.*?)\)(/(.*))?$') |
| 177 | 177 |
| 178 def SourceTreeAndPathFromPath(input_path): | 178 def SourceTreeAndPathFromPath(input_path): |
| 179 """Given input_path, returns a tuple with sourceTree and path values. | 179 """Given input_path, returns a tuple with sourceTree and path values. |
| 180 | 180 |
| 181 Examples: | 181 Examples: |
| 182 input_path (source_tree, output_path) | 182 input_path (source_tree, output_path) |
| 183 '$(VAR)/path' ('VAR', 'path') | 183 '$(VAR)/path' ('VAR', 'path') |
| 184 '$(VAR)' ('VAR', None) | 184 '$(VAR)' ('VAR', None) |
| 185 'path' (None, 'path') | 185 'path' (None, 'path') |
| 186 """ | 186 """ |
| 187 | 187 |
| 188 source_group_match = _path_leading_variable.match(input_path) | 188 source_group_match = _path_leading_variable.match(input_path) |
| 189 if source_group_match: | 189 if source_group_match: |
| 190 source_tree = source_group_match.group(1) | 190 source_tree = source_group_match.group(1) |
| 191 output_path = source_group_match.group(3) # This may be None. | 191 output_path = source_group_match.group(3) # This may be None. |
| 192 else: | 192 else: |
| 193 source_tree = None | 193 source_tree = None |
| 194 output_path = input_path | 194 output_path = input_path |
| 195 | 195 |
| 196 return (source_tree, output_path) | 196 return (source_tree, output_path) |
| 197 | 197 |
| 198 def ConvertVariablesToShellSyntax(input_string): | 198 def ConvertVariablesToShellSyntax(input_string): |
| 199 return re.sub('\$\((.*?)\)', '${\\1}', input_string) | 199 return re.sub(r'\$\((.*?)\)', '${\\1}', input_string) |
| 200 | 200 |
| 201 class XCObject(object): | 201 class XCObject(object): |
| 202 """The abstract base of all class types used in Xcode project files. | 202 """The abstract base of all class types used in Xcode project files. |
| 203 | 203 |
| 204 Class variables: | 204 Class variables: |
| 205 _schema: A dictionary defining the properties of this class. The keys to | 205 _schema: A dictionary defining the properties of this class. The keys to |
| 206 _schema are string property keys as used in project files. Values | 206 _schema are string property keys as used in project files. Values |
| 207 are a list of four or five elements: | 207 are a list of four or five elements: |
| 208 [ is_list, property_type, is_strong, is_required, default ] | 208 [ is_list, property_type, is_strong, is_required, default ] |
| 209 is_list: True if the property described is a list, as opposed | 209 is_list: True if the property described is a list, as opposed |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 334 that._properties[key] = [] | 334 that._properties[key] = [] |
| 335 for item in value: | 335 for item in value: |
| 336 new_item = item.Copy() | 336 new_item = item.Copy() |
| 337 new_item.parent = that | 337 new_item.parent = that |
| 338 that._properties[key].append(new_item) | 338 that._properties[key].append(new_item) |
| 339 else: | 339 else: |
| 340 that._properties[key] = value[:] | 340 that._properties[key] = value[:] |
| 341 elif isinstance(value, dict): | 341 elif isinstance(value, dict): |
| 342 # dicts are never strong. | 342 # dicts are never strong. |
| 343 if is_strong: | 343 if is_strong: |
| 344 raise TypeError, 'Strong dict for key ' + key + ' in ' + \ | 344 raise TypeError('Strong dict for key ' + key + ' in ' + \ |
| 345 self.__class__.__name__ | 345 self.__class__.__name__) |
| 346 else: | 346 else: |
| 347 that._properties[key] = value.copy() | 347 that._properties[key] = value.copy() |
| 348 else: | 348 else: |
| 349 raise TypeError, 'Unexpected type ' + value.__class__.__name__ + \ | 349 raise TypeError('Unexpected type ' + value.__class__.__name__ + \ |
| 350 ' for key ' + key + ' in ' + self.__class__.__name__ | 350 ' for key ' + key + ' in ' + self.__class__.__name__) |
| 351 | 351 |
| 352 return that | 352 return that |
| 353 | 353 |
| 354 def Name(self): | 354 def Name(self): |
| 355 """Return the name corresponding to an object. | 355 """Return the name corresponding to an object. |
| 356 | 356 |
| 357 Not all objects necessarily need to be nameable, and not all that do have | 357 Not all objects necessarily need to be nameable, and not all that do have |
| 358 a "name" property. Override as needed. | 358 a "name" property. Override as needed. |
| 359 """ | 359 """ |
| 360 | 360 |
| 361 # If the schema indicates that "name" is required, try to access the | 361 # If the schema indicates that "name" is required, try to access the |
| 362 # property even if it doesn't exist. This will result in a KeyError | 362 # property even if it doesn't exist. This will result in a KeyError |
| 363 # being raised for the property that should be present, which seems more | 363 # being raised for the property that should be present, which seems more |
| 364 # appropriate than NotImplementedError in this case. | 364 # appropriate than NotImplementedError in this case. |
| 365 if 'name' in self._properties or \ | 365 if 'name' in self._properties or \ |
| 366 ('name' in self._schema and self._schema['name'][3]): | 366 ('name' in self._schema and self._schema['name'][3]): |
| 367 return self._properties['name'] | 367 return self._properties['name'] |
| 368 | 368 |
| 369 raise NotImplementedError, \ | 369 raise NotImplementedError(self.__class__.__name__ + ' must implement Name') |
| 370 self.__class__.__name__ + ' must implement Name' | |
| 371 | 370 |
| 372 def Comment(self): | 371 def Comment(self): |
| 373 """Return a comment string for the object. | 372 """Return a comment string for the object. |
| 374 | 373 |
| 375 Most objects just use their name as the comment, but PBXProject uses | 374 Most objects just use their name as the comment, but PBXProject uses |
| 376 different values. | 375 different values. |
| 377 | 376 |
| 378 The returned comment is not escaped and does not have any comment marker | 377 The returned comment is not escaped and does not have any comment marker |
| 379 strings applied to it. | 378 strings applied to it. |
| 380 """ | 379 """ |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 459 | 458 |
| 460 def EnsureNoIDCollisions(self): | 459 def EnsureNoIDCollisions(self): |
| 461 """Verifies that no two objects have the same ID. Checks all descendants. | 460 """Verifies that no two objects have the same ID. Checks all descendants. |
| 462 """ | 461 """ |
| 463 | 462 |
| 464 ids = {} | 463 ids = {} |
| 465 descendants = self.Descendants() | 464 descendants = self.Descendants() |
| 466 for descendant in descendants: | 465 for descendant in descendants: |
| 467 if descendant.id in ids: | 466 if descendant.id in ids: |
| 468 other = ids[descendant.id] | 467 other = ids[descendant.id] |
| 469 raise KeyError, \ | 468 raise KeyError( |
| 470 'Duplicate ID %s, objects "%s" and "%s" in "%s"' % \ | 469 'Duplicate ID %s, objects "%s" and "%s" in "%s"' % \ |
| 471 (descendant.id, str(descendant._properties), | 470 (descendant.id, str(descendant._properties), |
| 472 str(other._properties), self._properties['rootObject'].Name()) | 471 str(other._properties), self._properties['rootObject'].Name())) |
| 473 ids[descendant.id] = descendant | 472 ids[descendant.id] = descendant |
| 474 | 473 |
| 475 def Children(self): | 474 def Children(self): |
| 476 """Returns a list of all of this object's owned (strong) children.""" | 475 """Returns a list of all of this object's owned (strong) children.""" |
| 477 | 476 |
| 478 children = [] | 477 children = [] |
| 479 for property, attributes in self._schema.iteritems(): | 478 for property, attributes in self._schema.iteritems(): |
| 480 (is_list, property_type, is_strong) = attributes[0:3] | 479 (is_list, property_type, is_strong) = attributes[0:3] |
| 481 if is_strong and property in self._properties: | 480 if is_strong and property in self._properties: |
| 482 if not is_list: | 481 if not is_list: |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 623 printable += end_tabs + ')' | 622 printable += end_tabs + ')' |
| 624 elif isinstance(value, dict): | 623 elif isinstance(value, dict): |
| 625 printable = '{' + sep | 624 printable = '{' + sep |
| 626 for item_key, item_value in sorted(value.iteritems()): | 625 for item_key, item_value in sorted(value.iteritems()): |
| 627 printable += element_tabs + \ | 626 printable += element_tabs + \ |
| 628 self._XCPrintableValue(tabs + 1, item_key, flatten_list) + ' = ' + \ | 627 self._XCPrintableValue(tabs + 1, item_key, flatten_list) + ' = ' + \ |
| 629 self._XCPrintableValue(tabs + 1, item_value, flatten_list) + ';' + \ | 628 self._XCPrintableValue(tabs + 1, item_value, flatten_list) + ';' + \ |
| 630 sep | 629 sep |
| 631 printable += end_tabs + '}' | 630 printable += end_tabs + '}' |
| 632 else: | 631 else: |
| 633 raise TypeError, "Can't make " + value.__class__.__name__ + ' printable' | 632 raise TypeError("Can't make " + value.__class__.__name__ + ' printable') |
| 634 | 633 |
| 635 if comment != None: | 634 if comment != None: |
| 636 printable += ' ' + self._EncodeComment(comment) | 635 printable += ' ' + self._EncodeComment(comment) |
| 637 | 636 |
| 638 return printable | 637 return printable |
| 639 | 638 |
| 640 def _XCKVPrint(self, file, tabs, key, value): | 639 def _XCKVPrint(self, file, tabs, key, value): |
| 641 """Prints a key and value, members of an XCObject's _properties dictionary, | 640 """Prints a key and value, members of an XCObject's _properties dictionary, |
| 642 to file. | 641 to file. |
| 643 | 642 |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 749 strong-owned XCObjects in lists will be copied instead of having their | 748 strong-owned XCObjects in lists will be copied instead of having their |
| 750 references added. | 749 references added. |
| 751 """ | 750 """ |
| 752 | 751 |
| 753 if properties is None: | 752 if properties is None: |
| 754 return | 753 return |
| 755 | 754 |
| 756 for property, value in properties.iteritems(): | 755 for property, value in properties.iteritems(): |
| 757 # Make sure the property is in the schema. | 756 # Make sure the property is in the schema. |
| 758 if not property in self._schema: | 757 if not property in self._schema: |
| 759 raise KeyError, property + ' not in ' + self.__class__.__name__ | 758 raise KeyError(property + ' not in ' + self.__class__.__name__) |
| 760 | 759 |
| 761 # Make sure the property conforms to the schema. | 760 # Make sure the property conforms to the schema. |
| 762 (is_list, property_type, is_strong) = self._schema[property][0:3] | 761 (is_list, property_type, is_strong) = self._schema[property][0:3] |
| 763 if is_list: | 762 if is_list: |
| 764 if value.__class__ != list: | 763 if value.__class__ != list: |
| 765 raise TypeError, \ | 764 raise TypeError( |
| 766 property + ' of ' + self.__class__.__name__ + \ | 765 property + ' of ' + self.__class__.__name__ + \ |
| 767 ' must be list, not ' + value.__class__.__name__ | 766 ' must be list, not ' + value.__class__.__name__) |
| 768 for item in value: | 767 for item in value: |
| 769 if not isinstance(item, property_type) and \ | 768 if not isinstance(item, property_type) and \ |
| 770 not (item.__class__ == unicode and property_type == str): | 769 not (item.__class__ == unicode and property_type == str): |
| 771 # Accept unicode where str is specified. str is treated as | 770 # Accept unicode where str is specified. str is treated as |
| 772 # UTF-8-encoded. | 771 # UTF-8-encoded. |
| 773 raise TypeError, \ | 772 raise TypeError( |
| 774 'item of ' + property + ' of ' + self.__class__.__name__ + \ | 773 'item of ' + property + ' of ' + self.__class__.__name__ + \ |
| 775 ' must be ' + property_type.__name__ + ', not ' + \ | 774 ' must be ' + property_type.__name__ + ', not ' + \ |
| 776 item.__class__.__name__ | 775 item.__class__.__name__) |
| 777 elif not isinstance(value, property_type) and \ | 776 elif not isinstance(value, property_type) and \ |
| 778 not (value.__class__ == unicode and property_type == str): | 777 not (value.__class__ == unicode and property_type == str): |
| 779 # Accept unicode where str is specified. str is treated as | 778 # Accept unicode where str is specified. str is treated as |
| 780 # UTF-8-encoded. | 779 # UTF-8-encoded. |
| 781 raise TypeError, \ | 780 raise TypeError( |
| 782 property + ' of ' + self.__class__.__name__ + ' must be ' + \ | 781 property + ' of ' + self.__class__.__name__ + ' must be ' + \ |
| 783 property_type.__name__ + ', not ' + value.__class__.__name__ | 782 property_type.__name__ + ', not ' + value.__class__.__name__) |
| 784 | 783 |
| 785 # Checks passed, perform the assignment. | 784 # Checks passed, perform the assignment. |
| 786 if do_copy: | 785 if do_copy: |
| 787 if isinstance(value, XCObject): | 786 if isinstance(value, XCObject): |
| 788 if is_strong: | 787 if is_strong: |
| 789 self._properties[property] = value.Copy() | 788 self._properties[property] = value.Copy() |
| 790 else: | 789 else: |
| 791 self._properties[property] = value | 790 self._properties[property] = value |
| 792 elif isinstance(value, str) or isinstance(value, unicode) or \ | 791 elif isinstance(value, str) or isinstance(value, unicode) or \ |
| 793 isinstance(value, int): | 792 isinstance(value, int): |
| 794 self._properties[property] = value | 793 self._properties[property] = value |
| 795 elif isinstance(value, list): | 794 elif isinstance(value, list): |
| 796 if is_strong: | 795 if is_strong: |
| 797 # If is_strong is True, each element is an XCObject, so it's safe | 796 # If is_strong is True, each element is an XCObject, so it's safe |
| 798 # to call Copy. | 797 # to call Copy. |
| 799 self._properties[property] = [] | 798 self._properties[property] = [] |
| 800 for item in value: | 799 for item in value: |
| 801 self._properties[property].append(item.Copy()) | 800 self._properties[property].append(item.Copy()) |
| 802 else: | 801 else: |
| 803 self._properties[property] = value[:] | 802 self._properties[property] = value[:] |
| 804 elif isinstance(value, dict): | 803 elif isinstance(value, dict): |
| 805 self._properties[property] = value.copy() | 804 self._properties[property] = value.copy() |
| 806 else: | 805 else: |
| 807 raise TypeError, "Don't know how to copy a " + \ | 806 raise TypeError("Don't know how to copy a " + \ |
| 808 value.__class__.__name__ + ' object for ' + \ | 807 value.__class__.__name__ + ' object for ' + \ |
| 809 property + ' in ' + self.__class__.__name__ | 808 property + ' in ' + self.__class__.__name__) |
| 810 else: | 809 else: |
| 811 self._properties[property] = value | 810 self._properties[property] = value |
| 812 | 811 |
| 813 # Set up the child's back-reference to this object. Don't use |value| | 812 # Set up the child's back-reference to this object. Don't use |value| |
| 814 # any more because it may not be right if do_copy is true. | 813 # any more because it may not be right if do_copy is true. |
| 815 if is_strong: | 814 if is_strong: |
| 816 if not is_list: | 815 if not is_list: |
| 817 self._properties[property].parent = self | 816 self._properties[property].parent = self |
| 818 else: | 817 else: |
| 819 for item in self._properties[property]: | 818 for item in self._properties[property]: |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 830 | 829 |
| 831 def DelProperty(self, key): | 830 def DelProperty(self, key): |
| 832 if key in self._properties: | 831 if key in self._properties: |
| 833 del self._properties[key] | 832 del self._properties[key] |
| 834 | 833 |
| 835 def AppendProperty(self, key, value): | 834 def AppendProperty(self, key, value): |
| 836 # TODO(mark): Support ExtendProperty too (and make this call that)? | 835 # TODO(mark): Support ExtendProperty too (and make this call that)? |
| 837 | 836 |
| 838 # Schema validation. | 837 # Schema validation. |
| 839 if not key in self._schema: | 838 if not key in self._schema: |
| 840 raise KeyError, key + ' not in ' + self.__class__.__name__ | 839 raise KeyError(key + ' not in ' + self.__class__.__name__) |
| 841 | 840 |
| 842 (is_list, property_type, is_strong) = self._schema[key][0:3] | 841 (is_list, property_type, is_strong) = self._schema[key][0:3] |
| 843 if not is_list: | 842 if not is_list: |
| 844 raise TypeError, key + ' of ' + self.__class__.__name__ + ' must be list' | 843 raise TypeError(key + ' of ' + self.__class__.__name__ + ' must be list') |
| 845 if not isinstance(value, property_type): | 844 if not isinstance(value, property_type): |
| 846 raise TypeError, 'item of ' + key + ' of ' + self.__class__.__name__ + \ | 845 raise TypeError('item of ' + key + ' of ' + self.__class__.__name__ + \ |
| 847 ' must be ' + property_type.__name__ + ', not ' + \ | 846 ' must be ' + property_type.__name__ + ', not ' + \ |
| 848 value.__class__.__name__ | 847 value.__class__.__name__) |
| 849 | 848 |
| 850 # If the property doesn't exist yet, create a new empty list to receive the | 849 # If the property doesn't exist yet, create a new empty list to receive the |
| 851 # item. | 850 # item. |
| 852 if not key in self._properties: | 851 if not key in self._properties: |
| 853 self._properties[key] = [] | 852 self._properties[key] = [] |
| 854 | 853 |
| 855 # Set up the ownership link. | 854 # Set up the ownership link. |
| 856 if is_strong: | 855 if is_strong: |
| 857 value.parent = self | 856 value.parent = self |
| 858 | 857 |
| 859 # Store the item. | 858 # Store the item. |
| 860 self._properties[key].append(value) | 859 self._properties[key].append(value) |
| 861 | 860 |
| 862 def VerifyHasRequiredProperties(self): | 861 def VerifyHasRequiredProperties(self): |
| 863 """Ensure that all properties identified as required by the schema are | 862 """Ensure that all properties identified as required by the schema are |
| 864 set. | 863 set. |
| 865 """ | 864 """ |
| 866 | 865 |
| 867 # TODO(mark): A stronger verification mechanism is needed. Some | 866 # TODO(mark): A stronger verification mechanism is needed. Some |
| 868 # subclasses need to perform validation beyond what the schema can enforce. | 867 # subclasses need to perform validation beyond what the schema can enforce. |
| 869 for property, attributes in self._schema.iteritems(): | 868 for property, attributes in self._schema.iteritems(): |
| 870 (is_list, property_type, is_strong, is_required) = attributes[0:4] | 869 (is_list, property_type, is_strong, is_required) = attributes[0:4] |
| 871 if is_required and not property in self._properties: | 870 if is_required and not property in self._properties: |
| 872 raise KeyError, self.__class__.__name__ + ' requires ' + property | 871 raise KeyError(self.__class__.__name__ + ' requires ' + property) |
| 873 | 872 |
| 874 def _SetDefaultsFromSchema(self): | 873 def _SetDefaultsFromSchema(self): |
| 875 """Assign object default values according to the schema. This will not | 874 """Assign object default values according to the schema. This will not |
| 876 overwrite properties that have already been set.""" | 875 overwrite properties that have already been set.""" |
| 877 | 876 |
| 878 defaults = {} | 877 defaults = {} |
| 879 for property, attributes in self._schema.iteritems(): | 878 for property, attributes in self._schema.iteritems(): |
| 880 (is_list, property_type, is_strong, is_required) = attributes[0:4] | 879 (is_list, property_type, is_strong, is_required) = attributes[0:4] |
| 881 if is_required and len(attributes) >= 5 and \ | 880 if is_required and len(attributes) >= 5 and \ |
| 882 not property in self._properties: | 881 not property in self._properties: |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1136 def HashablesForChild(self): | 1135 def HashablesForChild(self): |
| 1137 # To avoid a circular reference the hashables used to compute a child id do | 1136 # To avoid a circular reference the hashables used to compute a child id do |
| 1138 # not include the child names. | 1137 # not include the child names. |
| 1139 return XCHierarchicalElement.Hashables(self) | 1138 return XCHierarchicalElement.Hashables(self) |
| 1140 | 1139 |
| 1141 def _AddChildToDicts(self, child): | 1140 def _AddChildToDicts(self, child): |
| 1142 # Sets up this PBXGroup object's dicts to reference the child properly. | 1141 # Sets up this PBXGroup object's dicts to reference the child properly. |
| 1143 child_path = child.PathFromSourceTreeAndPath() | 1142 child_path = child.PathFromSourceTreeAndPath() |
| 1144 if child_path: | 1143 if child_path: |
| 1145 if child_path in self._children_by_path: | 1144 if child_path in self._children_by_path: |
| 1146 raise ValueError, 'Found multiple children with path ' + child_path | 1145 raise ValueError('Found multiple children with path ' + child_path) |
| 1147 self._children_by_path[child_path] = child | 1146 self._children_by_path[child_path] = child |
| 1148 | 1147 |
| 1149 if isinstance(child, PBXVariantGroup): | 1148 if isinstance(child, PBXVariantGroup): |
| 1150 child_name = child._properties.get('name', None) | 1149 child_name = child._properties.get('name', None) |
| 1151 key = (child_name, child_path) | 1150 key = (child_name, child_path) |
| 1152 if key in self._variant_children_by_name_and_path: | 1151 if key in self._variant_children_by_name_and_path: |
| 1153 raise ValueError, 'Found multiple PBXVariantGroup children with ' + \ | 1152 raise ValueError('Found multiple PBXVariantGroup children with ' + \ |
| 1154 'name ' + str(child_name) + ' and path ' + \ | 1153 'name ' + str(child_name) + ' and path ' + \ |
| 1155 str(child_path) | 1154 str(child_path)) |
| 1156 self._variant_children_by_name_and_path[key] = child | 1155 self._variant_children_by_name_and_path[key] = child |
| 1157 | 1156 |
| 1158 def AppendChild(self, child): | 1157 def AppendChild(self, child): |
| 1159 # Callers should use this instead of calling | 1158 # Callers should use this instead of calling |
| 1160 # AppendProperty('children', child) directly because this function | 1159 # AppendProperty('children', child) directly because this function |
| 1161 # maintains the group's dicts. | 1160 # maintains the group's dicts. |
| 1162 self.AppendProperty('children', child) | 1161 self.AppendProperty('children', child) |
| 1163 self._AddChildToDicts(child) | 1162 self._AddChildToDicts(child) |
| 1164 | 1163 |
| 1165 def GetChildByName(self, name): | 1164 def GetChildByName(self, name): |
| (...skipping 435 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1601 def Name(self): | 1600 def Name(self): |
| 1602 return 'Build configuration list for ' + \ | 1601 return 'Build configuration list for ' + \ |
| 1603 self.parent.__class__.__name__ + ' "' + self.parent.Name() + '"' | 1602 self.parent.__class__.__name__ + ' "' + self.parent.Name() + '"' |
| 1604 | 1603 |
| 1605 def ConfigurationNamed(self, name): | 1604 def ConfigurationNamed(self, name): |
| 1606 """Convenience accessor to obtain an XCBuildConfiguration by name.""" | 1605 """Convenience accessor to obtain an XCBuildConfiguration by name.""" |
| 1607 for configuration in self._properties['buildConfigurations']: | 1606 for configuration in self._properties['buildConfigurations']: |
| 1608 if configuration._properties['name'] == name: | 1607 if configuration._properties['name'] == name: |
| 1609 return configuration | 1608 return configuration |
| 1610 | 1609 |
| 1611 raise KeyError, name | 1610 raise KeyError(name) |
| 1612 | 1611 |
| 1613 def DefaultConfiguration(self): | 1612 def DefaultConfiguration(self): |
| 1614 """Convenience accessor to obtain the default XCBuildConfiguration.""" | 1613 """Convenience accessor to obtain the default XCBuildConfiguration.""" |
| 1615 return self.ConfigurationNamed(self._properties['defaultConfigurationName']) | 1614 return self.ConfigurationNamed(self._properties['defaultConfigurationName']) |
| 1616 | 1615 |
| 1617 def HasBuildSetting(self, key): | 1616 def HasBuildSetting(self, key): |
| 1618 """Determines the state of a build setting in all XCBuildConfiguration | 1617 """Determines the state of a build setting in all XCBuildConfiguration |
| 1619 child objects. | 1618 child objects. |
| 1620 | 1619 |
| 1621 If all child objects have key in their build settings, and the value is the | 1620 If all child objects have key in their build settings, and the value is the |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1658 # TODO(mark): This is wrong for build settings that are lists. The list | 1657 # TODO(mark): This is wrong for build settings that are lists. The list |
| 1659 # contents should be compared (and a list copy returned?) | 1658 # contents should be compared (and a list copy returned?) |
| 1660 | 1659 |
| 1661 value = None | 1660 value = None |
| 1662 for configuration in self._properties['buildConfigurations']: | 1661 for configuration in self._properties['buildConfigurations']: |
| 1663 configuration_value = configuration.GetBuildSetting(key) | 1662 configuration_value = configuration.GetBuildSetting(key) |
| 1664 if value is None: | 1663 if value is None: |
| 1665 value = configuration_value | 1664 value = configuration_value |
| 1666 else: | 1665 else: |
| 1667 if value != configuration_value: | 1666 if value != configuration_value: |
| 1668 raise ValueError, 'Variant values for ' + key | 1667 raise ValueError('Variant values for ' + key) |
| 1669 | 1668 |
| 1670 return value | 1669 return value |
| 1671 | 1670 |
| 1672 def SetBuildSetting(self, key, value): | 1671 def SetBuildSetting(self, key, value): |
| 1673 """Sets the build setting for key to value in all child | 1672 """Sets the build setting for key to value in all child |
| 1674 XCBuildConfiguration objects. | 1673 XCBuildConfiguration objects. |
| 1675 """ | 1674 """ |
| 1676 | 1675 |
| 1677 for configuration in self._properties['buildConfigurations']: | 1676 for configuration in self._properties['buildConfigurations']: |
| 1678 configuration.SetBuildSetting(key, value) | 1677 configuration.SetBuildSetting(key, value) |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1765 self._files_by_xcfilelikeelement = {} | 1764 self._files_by_xcfilelikeelement = {} |
| 1766 for pbxbuildfile in self._properties.get('files', []): | 1765 for pbxbuildfile in self._properties.get('files', []): |
| 1767 self._AddBuildFileToDicts(pbxbuildfile) | 1766 self._AddBuildFileToDicts(pbxbuildfile) |
| 1768 | 1767 |
| 1769 def FileGroup(self, path): | 1768 def FileGroup(self, path): |
| 1770 # Subclasses must override this by returning a two-element tuple. The | 1769 # Subclasses must override this by returning a two-element tuple. The |
| 1771 # first item in the tuple should be the PBXGroup to which "path" should be | 1770 # first item in the tuple should be the PBXGroup to which "path" should be |
| 1772 # added, either as a child or deeper descendant. The second item should | 1771 # added, either as a child or deeper descendant. The second item should |
| 1773 # be a boolean indicating whether files should be added into hierarchical | 1772 # be a boolean indicating whether files should be added into hierarchical |
| 1774 # groups or one single flat group. | 1773 # groups or one single flat group. |
| 1775 raise NotImplementedError, \ | 1774 raise NotImplementedError( |
| 1776 self.__class__.__name__ + ' must implement FileGroup' | 1775 self.__class__.__name__ + ' must implement FileGroup') |
| 1777 | 1776 |
| 1778 def _AddPathToDict(self, pbxbuildfile, path): | 1777 def _AddPathToDict(self, pbxbuildfile, path): |
| 1779 """Adds path to the dict tracking paths belonging to this build phase. | 1778 """Adds path to the dict tracking paths belonging to this build phase. |
| 1780 | 1779 |
| 1781 If the path is already a member of this build phase, raises an exception. | 1780 If the path is already a member of this build phase, raises an exception. |
| 1782 """ | 1781 """ |
| 1783 | 1782 |
| 1784 if path in self._files_by_path: | 1783 if path in self._files_by_path: |
| 1785 raise ValueError, 'Found multiple build files with path ' + path | 1784 raise ValueError('Found multiple build files with path ' + path) |
| 1786 self._files_by_path[path] = pbxbuildfile | 1785 self._files_by_path[path] = pbxbuildfile |
| 1787 | 1786 |
| 1788 def _AddBuildFileToDicts(self, pbxbuildfile, path=None): | 1787 def _AddBuildFileToDicts(self, pbxbuildfile, path=None): |
| 1789 """Maintains the _files_by_path and _files_by_xcfilelikeelement dicts. | 1788 """Maintains the _files_by_path and _files_by_xcfilelikeelement dicts. |
| 1790 | 1789 |
| 1791 If path is specified, then it is the path that is being added to the | 1790 If path is specified, then it is the path that is being added to the |
| 1792 phase, and pbxbuildfile must contain either a PBXFileReference directly | 1791 phase, and pbxbuildfile must contain either a PBXFileReference directly |
| 1793 referencing that path, or it must contain a PBXVariantGroup that itself | 1792 referencing that path, or it must contain a PBXVariantGroup that itself |
| 1794 contains a PBXFileReference referencing the path. | 1793 contains a PBXFileReference referencing the path. |
| 1795 | 1794 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1830 # Add the paths first, because if something's going to raise, the | 1829 # Add the paths first, because if something's going to raise, the |
| 1831 # messages provided by _AddPathToDict are more useful owing to its | 1830 # messages provided by _AddPathToDict are more useful owing to its |
| 1832 # having access to a real pathname and not just an object's Name(). | 1831 # having access to a real pathname and not just an object's Name(). |
| 1833 for a_path in paths: | 1832 for a_path in paths: |
| 1834 self._AddPathToDict(pbxbuildfile, a_path) | 1833 self._AddPathToDict(pbxbuildfile, a_path) |
| 1835 | 1834 |
| 1836 # If another PBXBuildFile references this XCFileLikeElement, there's a | 1835 # If another PBXBuildFile references this XCFileLikeElement, there's a |
| 1837 # problem. | 1836 # problem. |
| 1838 if xcfilelikeelement in self._files_by_xcfilelikeelement and \ | 1837 if xcfilelikeelement in self._files_by_xcfilelikeelement and \ |
| 1839 self._files_by_xcfilelikeelement[xcfilelikeelement] != pbxbuildfile: | 1838 self._files_by_xcfilelikeelement[xcfilelikeelement] != pbxbuildfile: |
| 1840 raise ValueError, 'Found multiple build files for ' + \ | 1839 raise ValueError('Found multiple build files for ' + \ |
| 1841 xcfilelikeelement.Name() | 1840 xcfilelikeelement.Name()) |
| 1842 self._files_by_xcfilelikeelement[xcfilelikeelement] = pbxbuildfile | 1841 self._files_by_xcfilelikeelement[xcfilelikeelement] = pbxbuildfile |
| 1843 | 1842 |
| 1844 def AppendBuildFile(self, pbxbuildfile, path=None): | 1843 def AppendBuildFile(self, pbxbuildfile, path=None): |
| 1845 # Callers should use this instead of calling | 1844 # Callers should use this instead of calling |
| 1846 # AppendProperty('files', pbxbuildfile) directly because this function | 1845 # AppendProperty('files', pbxbuildfile) directly because this function |
| 1847 # maintains the object's dicts. Better yet, callers can just call AddFile | 1846 # maintains the object's dicts. Better yet, callers can just call AddFile |
| 1848 # with a pathname and not worry about building their own PBXBuildFile | 1847 # with a pathname and not worry about building their own PBXBuildFile |
| 1849 # objects. | 1848 # objects. |
| 1850 self.AppendProperty('files', pbxbuildfile) | 1849 self.AppendProperty('files', pbxbuildfile) |
| 1851 self._AddBuildFileToDicts(pbxbuildfile, path) | 1850 self._AddBuildFileToDicts(pbxbuildfile, path) |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1995 # The path starts with an unrecognized Xcode variable | 1994 # The path starts with an unrecognized Xcode variable |
| 1996 # name like $(SRCROOT). Xcode will still handle this | 1995 # name like $(SRCROOT). Xcode will still handle this |
| 1997 # as an "absolute path" that starts with the variable. | 1996 # as an "absolute path" that starts with the variable. |
| 1998 subfolder = 0 | 1997 subfolder = 0 |
| 1999 relative_path = path | 1998 relative_path = path |
| 2000 elif path.startswith('/'): | 1999 elif path.startswith('/'): |
| 2001 # Special case. Absolute paths are in dstSubfolderSpec 0. | 2000 # Special case. Absolute paths are in dstSubfolderSpec 0. |
| 2002 subfolder = 0 | 2001 subfolder = 0 |
| 2003 relative_path = path[1:] | 2002 relative_path = path[1:] |
| 2004 else: | 2003 else: |
| 2005 raise ValueError, 'Can\'t use path %s in a %s' % \ | 2004 raise ValueError('Can\'t use path %s in a %s' % \ |
| 2006 (path, self.__class__.__name__) | 2005 (path, self.__class__.__name__)) |
| 2007 | 2006 |
| 2008 self._properties['dstPath'] = relative_path | 2007 self._properties['dstPath'] = relative_path |
| 2009 self._properties['dstSubfolderSpec'] = subfolder | 2008 self._properties['dstSubfolderSpec'] = subfolder |
| 2010 | 2009 |
| 2011 | 2010 |
| 2012 class PBXBuildRule(XCObject): | 2011 class PBXBuildRule(XCObject): |
| 2013 _schema = XCObject._schema.copy() | 2012 _schema = XCObject._schema.copy() |
| 2014 _schema.update({ | 2013 _schema.update({ |
| 2015 'compilerSpec': [0, str, 0, 1], | 2014 'compilerSpec': [0, str, 0, 1], |
| 2016 'filePatterns': [0, str, 0, 0], | 2015 'filePatterns': [0, str, 0, 0], |
| (...skipping 783 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2800 for target in other_pbxproject._properties['targets']: | 2799 for target in other_pbxproject._properties['targets']: |
| 2801 if not isinstance(target, PBXNativeTarget): | 2800 if not isinstance(target, PBXNativeTarget): |
| 2802 continue | 2801 continue |
| 2803 remote_products.append(target._properties['productReference']) | 2802 remote_products.append(target._properties['productReference']) |
| 2804 | 2803 |
| 2805 # Sort the PBXReferenceProxy children according to the list of remote | 2804 # Sort the PBXReferenceProxy children according to the list of remote |
| 2806 # products. | 2805 # products. |
| 2807 product_group = ref_dict['ProductGroup'] | 2806 product_group = ref_dict['ProductGroup'] |
| 2808 product_group._properties['children'] = sorted( | 2807 product_group._properties['children'] = sorted( |
| 2809 product_group._properties['children'], | 2808 product_group._properties['children'], |
| 2810 cmp=lambda x, y: CompareProducts(x, y, remote_products)) | 2809 cmp=lambda x, y, rp=remote_products: CompareProducts(x, y, rp)) |
|
scottmg
2014/11/20 07:29:12
this seems a bit funny, cmp for sorted should() be
Shezan Baig (Bloomberg)
2014/11/20 07:48:22
It complains that 'remote_products' is set inside
| |
| 2811 | 2810 |
| 2812 | 2811 |
| 2813 class XCProjectFile(XCObject): | 2812 class XCProjectFile(XCObject): |
| 2814 _schema = XCObject._schema.copy() | 2813 _schema = XCObject._schema.copy() |
| 2815 _schema.update({ | 2814 _schema.update({ |
| 2816 'archiveVersion': [0, int, 0, 1, 1], | 2815 'archiveVersion': [0, int, 0, 1, 1], |
| 2817 'classes': [0, dict, 0, 1, {}], | 2816 'classes': [0, dict, 0, 1, {}], |
| 2818 'objectVersion': [0, int, 0, 1, 45], | 2817 'objectVersion': [0, int, 0, 1, 45], |
| 2819 'rootObject': [0, PBXProject, 1, 1], | 2818 'rootObject': [0, PBXProject, 1, 1], |
| 2820 }) | 2819 }) |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2884 self._XCPrint(file, 0, '/* Begin ' + class_name + ' section */\n') | 2883 self._XCPrint(file, 0, '/* Begin ' + class_name + ' section */\n') |
| 2885 for object in sorted(objects_by_class[class_name], | 2884 for object in sorted(objects_by_class[class_name], |
| 2886 cmp=lambda x, y: cmp(x.id, y.id)): | 2885 cmp=lambda x, y: cmp(x.id, y.id)): |
| 2887 object.Print(file) | 2886 object.Print(file) |
| 2888 self._XCPrint(file, 0, '/* End ' + class_name + ' section */\n') | 2887 self._XCPrint(file, 0, '/* End ' + class_name + ' section */\n') |
| 2889 | 2888 |
| 2890 if self._should_print_single_line: | 2889 if self._should_print_single_line: |
| 2891 self._XCPrint(file, 0, '}; ') | 2890 self._XCPrint(file, 0, '}; ') |
| 2892 else: | 2891 else: |
| 2893 self._XCPrint(file, 1, '};\n') | 2892 self._XCPrint(file, 1, '};\n') |
| OLD | NEW |