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

Side by Side Diff: tools/xcodebodge/xcodebodge.py

Issue 20080: Make xcodebodge even more friendly by making list_native_targets (Closed)
Patch Set: Created 11 years, 10 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 | « no previous file | 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/python 1 #!/usr/bin/python
2 # Copyright (c) 2008 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2008 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 Commandline modification of Xcode project files 6 Commandline modification of Xcode project files
7 """ 7 """
8 8
9 import sys 9 import sys
10 import os 10 import os
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
166 # Read in the section, using custom classes where we need them 166 # Read in the section, using custom classes where we need them
167 section_end_match = self.__class__.SECTION_END_RE.match( 167 section_end_match = self.__class__.SECTION_END_RE.match(
168 self._raw_content[parse_line_no]) 168 self._raw_content[parse_line_no])
169 while not section_end_match: 169 while not section_end_match:
170 # Unhandled lines 170 # Unhandled lines
171 content = self._raw_content[parse_line_no] 171 content = self._raw_content[parse_line_no]
172 # Sections we can parse line-by-line 172 # Sections we can parse line-by-line
173 if section in ('PBXBuildFile', 'PBXFileReference'): 173 if section in ('PBXBuildFile', 'PBXFileReference'):
174 content = eval('%s.FromContent(content)' % section) 174 content = eval('%s.FromContent(content)' % section)
175 # Multiline sections 175 # Multiline sections
176 elif section in ('PBXGroup', 'PBXVariantGroup', 'PBXProject', 176 elif section in ('PBXGroup', 'PBXVariantGroup', 'PBXProject',
177 'PBXNativeTarget', 'PBXSourcesBuildPhase'): 177 'PBXNativeTarget', 'PBXSourcesBuildPhase'):
178 # Accumulate lines 178 # Accumulate lines
179 content_lines = [] 179 content_lines = []
180 while 1: 180 while 1:
181 content_lines.append(content) 181 content_lines.append(content)
182 if content == '\t\t};\n': break 182 if content == '\t\t};\n': break
183 parse_line_no += 1 183 parse_line_no += 1
184 content = self._raw_content[parse_line_no] 184 content = self._raw_content[parse_line_no]
185 content = eval('%s.FromContent(content_lines)' % section) 185 content = eval('%s.FromContent(content_lines)' % section)
186 186
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
267 if not found_uuid: 267 if not found_uuid:
268 raise RuntimeError('XcodeProject group descent failed to find %s' % 268 raise RuntimeError('XcodeProject group descent failed to find %s' %
269 child_uuid) 269 child_uuid)
270 self._root_group = None 270 self._root_group = None
271 for group in self._sections['PBXGroup']: 271 for group in self._sections['PBXGroup']:
272 if group.uuid == self._root_group_uuid: 272 if group.uuid == self._root_group_uuid:
273 self._root_group = group 273 self._root_group = group
274 GroupPathRecurse(group, self.source_root_path) 274 GroupPathRecurse(group, self.source_root_path)
275 if not self._root_group: 275 if not self._root_group:
276 raise RuntimeError('XcodeProject failed to find root group by UUID') 276 raise RuntimeError('XcodeProject failed to find root group by UUID')
277 277
278 def FileContent(self): 278 def FileContent(self):
279 """Generate and return the project file content as a list of lines""" 279 """Generate and return the project file content as a list of lines"""
280 content = [] 280 content = []
281 content.extend(self._header[:-1]) 281 content.extend(self._header[:-1])
282 for section in self._section_order: 282 for section in self._section_order:
283 content.append('\n/* Begin %s section */\n' % section) 283 content.append('\n/* Begin %s section */\n' % section)
284 for section_content in self._sections[section]: 284 for section_content in self._sections[section]:
285 content.append(str(section_content)) 285 content.append(str(section_content))
286 content.append('/* End %s section */\n' % section) 286 content.append('/* End %s section */\n' % section)
287 content.extend(self._tail) 287 content.extend(self._tail)
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
386 Returns: 386 Returns:
387 PBXFileReference instance 387 PBXFileReference instance
388 """ 388 """
389 for file_ref in self._sections['PBXFileReference']: 389 for file_ref in self._sections['PBXFileReference']:
390 if file_ref.uuid == uuid: 390 if file_ref.uuid == uuid:
391 return file_ref 391 return file_ref
392 raise RuntimeError('Missing PBXFileReference for UUID "%s"' % uuid) 392 raise RuntimeError('Missing PBXFileReference for UUID "%s"' % uuid)
393 393
394 def RemoveSourceFileReference(self, file_ref): 394 def RemoveSourceFileReference(self, file_ref):
395 """Remove a source file's PBXFileReference from the project, cleaning up all 395 """Remove a source file's PBXFileReference from the project, cleaning up all
396 PBXGroup and PBXBuildFile references to that PBXFileReference and 396 PBXGroup and PBXBuildFile references to that PBXFileReference and
397 furthermore, removing any PBXBuildFiles from all PBXNativeTarget source 397 furthermore, removing any PBXBuildFiles from all PBXNativeTarget source
398 lists. 398 lists.
399 399
400 Args: 400 Args:
401 file_ref: PBXFileReference instance 401 file_ref: PBXFileReference instance
402 402
403 Raises: 403 Raises:
404 RuntimeError if |file_ref| is not a source file reference in PBXBuildFile 404 RuntimeError if |file_ref| is not a source file reference in PBXBuildFile
405 """ 405 """
406 self._sections['PBXFileReference'].remove(file_ref) 406 self._sections['PBXFileReference'].remove(file_ref)
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
470 """Convert a path to a group-relative path if possible 470 """Convert a path to a group-relative path if possible
471 471
472 Args: 472 Args:
473 abs_path: Absolute path to convert 473 abs_path: Absolute path to convert
474 474
475 Returns: 475 Returns:
476 Parent PBXGroup instance if possible or None 476 Parent PBXGroup instance if possible or None
477 """ 477 """
478 needed_path = os.path.dirname(abs_path) 478 needed_path = os.path.dirname(abs_path)
479 possible_groups = [ g for g in self._sections['PBXGroup'] 479 possible_groups = [ g for g in self._sections['PBXGroup']
480 if g.abs_path == needed_path and 480 if g.abs_path == needed_path and
481 not g.name in NON_SOURCE_GROUP_NAMES ] 481 not g.name in NON_SOURCE_GROUP_NAMES ]
482 if len(possible_groups) < 1: 482 if len(possible_groups) < 1:
483 return None 483 return None
484 elif len(possible_groups) == 1: 484 elif len(possible_groups) == 1:
485 return possible_groups[0] 485 return possible_groups[0]
486 # Multiple groups match, try to find the best using some simple 486 # Multiple groups match, try to find the best using some simple
487 # heuristics. Does only one group contain source? 487 # heuristics. Does only one group contain source?
488 groups_with_source = [] 488 groups_with_source = []
489 for group in possible_groups: 489 for group in possible_groups:
490 for child_uuid in group.child_uuids: 490 for child_uuid in group.child_uuids:
(...skipping 11 matching lines...) Expand all
502 if g is not self._root_group ] 502 if g is not self._root_group ]
503 if len(non_root_groups) == 1: 503 if len(non_root_groups) == 1:
504 return non_root_groups[0] 504 return non_root_groups[0]
505 # Best guess 505 # Best guess
506 if len(non_root_groups): 506 if len(non_root_groups):
507 return non_root_groups[0] 507 return non_root_groups[0]
508 elif len(groups_with_source): 508 elif len(groups_with_source):
509 return groups_with_source[0] 509 return groups_with_source[0]
510 else: 510 else:
511 return possible_groups[0] 511 return possible_groups[0]
512 512
513 def AddSourceFile(self, path): 513 def AddSourceFile(self, path):
514 """Add a source file to the project, attempting to position it 514 """Add a source file to the project, attempting to position it
515 in the GUI group heirarchy reasonably. 515 in the GUI group heirarchy reasonably.
516 516
517 NOTE: Adding a source file does not add it to any targets 517 NOTE: Adding a source file does not add it to any targets
518 518
519 Args: 519 Args:
520 path: Absolute path to the file to add 520 path: Absolute path to the file to add
521 521
522 Returns: 522 Returns:
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after
676 676
677 @classmethod 677 @classmethod
678 def FromContent(klass, content_line): 678 def FromContent(klass, content_line):
679 parsed = klass.PBXBUILDFILE_LINE_RE.match(content_line) 679 parsed = klass.PBXBUILDFILE_LINE_RE.match(content_line)
680 if not parsed: 680 if not parsed:
681 raise RuntimeError('PBXBuildFile unable to parse content:\n%s' 681 raise RuntimeError('PBXBuildFile unable to parse content:\n%s'
682 % content_line) 682 % content_line)
683 if parsed.group(2) != parsed.group(5): 683 if parsed.group(2) != parsed.group(5):
684 raise RuntimeError('PBXBuildFile name mismatch "%s" vs "%s"' % 684 raise RuntimeError('PBXBuildFile name mismatch "%s" vs "%s"' %
685 (parsed.group(2), parsed.group(5))) 685 (parsed.group(2), parsed.group(5)))
686 if not parsed.group(3) in ('Sources', 'Frameworks', 686 if not parsed.group(3) in ('Sources', 'Frameworks',
687 'Resources', 'CopyFiles', 687 'Resources', 'CopyFiles',
688 'Headers', 'Copy Into Framework', 688 'Headers', 'Copy Into Framework',
689 'Rez', 'Copy Generated Headers'): 689 'Rez', 'Copy Generated Headers'):
690 raise RuntimeError('PBXBuildFile unknown type "%s"' % parsed.group(3)) 690 raise RuntimeError('PBXBuildFile unknown type "%s"' % parsed.group(3))
691 return klass(parsed.group(1), parsed.group(2), parsed.group(3), 691 return klass(parsed.group(1), parsed.group(2), parsed.group(3),
692 parsed.group(4), parsed.group(6)) 692 parsed.group(4), parsed.group(6))
693 693
694 def __init__(self, uuid, name, type, file_ref_uuid, raw_extras): 694 def __init__(self, uuid, name, type, file_ref_uuid, raw_extras):
695 self.uuid = uuid 695 self.uuid = uuid
696 self.name = name 696 self.name = name
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
745 % content_line) 745 % content_line)
746 tree_parsed = klass.PBXFILEREFERENCE_SOURCETREE_RE.search(content_line) 746 tree_parsed = klass.PBXFILEREFERENCE_SOURCETREE_RE.search(content_line)
747 if not tree_parsed: 747 if not tree_parsed:
748 raise RuntimeError( 748 raise RuntimeError(
749 'PBXFileReference unable to parse source tree content:\n%s' 749 'PBXFileReference unable to parse source tree content:\n%s'
750 % content_line) 750 % content_line)
751 return klass(header_parsed.group(1), header_parsed.group(2), 751 return klass(header_parsed.group(1), header_parsed.group(2),
752 last_known_type, explicit_type, path_parsed.group(1), 752 last_known_type, explicit_type, path_parsed.group(1),
753 tree_parsed.group(1), content_line) 753 tree_parsed.group(1), content_line)
754 754
755 def __init__(self, uuid, name, last_known_file_type, explicit_file_type, 755 def __init__(self, uuid, name, last_known_file_type, explicit_file_type,
756 path, source_tree, raw_line): 756 path, source_tree, raw_line):
757 self.uuid = uuid 757 self.uuid = uuid
758 self.name = name 758 self.name = name
759 self._last_known_file_type = last_known_file_type 759 self._last_known_file_type = last_known_file_type
760 self._explicit_file_type = explicit_file_type 760 self._explicit_file_type = explicit_file_type
761 if explicit_file_type: 761 if explicit_file_type:
762 self.file_type = explicit_file_type 762 self.file_type = explicit_file_type
763 else: 763 else:
764 self.file_type = last_known_file_type 764 self.file_type = last_known_file_type
765 self.path = path 765 self.path = path
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
879 879
880 def __init__(self, uuid, name, path, source_tree, child_uuids, child_names, 880 def __init__(self, uuid, name, path, source_tree, child_uuids, child_names,
881 tab_width, uses_tabs, indent_width): 881 tab_width, uses_tabs, indent_width):
882 self.uuid = uuid 882 self.uuid = uuid
883 self.name = name 883 self.name = name
884 self.path = path 884 self.path = path
885 self.source_tree = source_tree 885 self.source_tree = source_tree
886 self.child_uuids = child_uuids 886 self.child_uuids = child_uuids
887 self.child_names = child_names 887 self.child_names = child_names
888 self.abs_path = None 888 self.abs_path = None
889 # Semantically I'm not sure these aren't an error, but they 889 # Semantically I'm not sure these aren't an error, but they
890 # appear in some projects 890 # appear in some projects
891 self._tab_width = tab_width 891 self._tab_width = tab_width
892 self._uses_tabs = uses_tabs 892 self._uses_tabs = uses_tabs
893 self._indent_width = indent_width 893 self._indent_width = indent_width
894 894
895 def __str__(self): 895 def __str__(self):
896 if self.name: 896 if self.name:
897 header_comment = '/* %s */ ' % self.name 897 header_comment = '/* %s */ ' % self.name
898 elif self.path: 898 elif self.path:
899 header_comment = '/* %s */ ' % self.path 899 header_comment = '/* %s */ ' % self.path
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
932 '\t\t\tchildren = (\n' \ 932 '\t\t\tchildren = (\n' \
933 '%s' \ 933 '%s' \
934 '\t\t\t);\n' \ 934 '\t\t\t);\n' \
935 '%s' \ 935 '%s' \
936 '%s' \ 936 '%s' \
937 '%s' \ 937 '%s' \
938 '\t\t\tsourceTree = %s;\n' \ 938 '\t\t\tsourceTree = %s;\n' \
939 '%s' \ 939 '%s' \
940 '%s' \ 940 '%s' \
941 '\t\t};\n' % ( 941 '\t\t};\n' % (
942 self.uuid, header_comment, 942 self.uuid, header_comment,
943 self.__class__.__name__, 943 self.__class__.__name__,
944 children, 944 children,
945 indent_width_attribute, 945 indent_width_attribute,
946 name_attribute, 946 name_attribute,
947 path_attribute, self.source_tree, 947 path_attribute, self.source_tree,
948 tab_width_attribute, uses_tabs_attribute) 948 tab_width_attribute, uses_tabs_attribute)
949 949
950 950
951 class PBXVariantGroup(PBXGroup): 951 class PBXVariantGroup(PBXGroup):
952 pass 952 pass
953 953
954 954
955 class PBXNativeTarget(object): 955 class PBXNativeTarget(object):
956 """Class for PBXNativeTarget data from an Xcode project file. 956 """Class for PBXNativeTarget data from an Xcode project file.
957 957
958 Attributes: 958 Attributes:
959 name: Target name 959 name: Target name
960 build_phase_uuids: Ordered list of build phase UUIDs 960 build_phase_uuids: Ordered list of build phase UUIDs
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
1152 project_path = os.path.join(project_path, 'project.pbxproj') 1152 project_path = os.path.join(project_path, 'project.pbxproj')
1153 if not project_path.endswith(os.sep + 'project.pbxproj'): 1153 if not project_path.endswith(os.sep + 'project.pbxproj'):
1154 option_parser.error('Invalid Xcode project file path \"%s\"' % project_path) 1154 option_parser.error('Invalid Xcode project file path \"%s\"' % project_path)
1155 if not os.path.exists(project_path): 1155 if not os.path.exists(project_path):
1156 option_parser.error('Missing Xcode project file \"%s\"' % project_path) 1156 option_parser.error('Missing Xcode project file \"%s\"' % project_path)
1157 1157
1158 # Construct project object 1158 # Construct project object
1159 project = XcodeProject(project_path) 1159 project = XcodeProject(project_path)
1160 1160
1161 # Switch on command 1161 # Switch on command
1162 if len(args) < 1:
1163 Usage(option_parser)
1164 1162
1165 # List native target names 1163 # List native target names (default command)
1166 elif args[0] == 'list_native_targets': 1164 if len(args) < 1 or args[0] == 'list_native_targets':
1167 # List targets
1168 if len(args) != 1:
1169 option_parser.error('list_native_targets takes no arguments')
1170 # Ape xcodebuild output 1165 # Ape xcodebuild output
1171 target_names = [] 1166 target_names = []
1172 for target in project.NativeTargets(): 1167 for target in project.NativeTargets():
1173 target_names.append(target.name) 1168 target_names.append(target.name)
1174 print 'Information about project "%s"\n Native Targets:\n %s' % ( 1169 print 'Information about project "%s"\n Native Targets:\n %s' % (
1175 project.name, 1170 project.name,
1176 '\n '.join(target_names)) 1171 '\n '.join(target_names))
1177 1172
1173 if len(args) < 1:
1174 # Be friendly and print some hints for further actions.
1175 print
1176 print 'To add or remove files from given target, run:'
1177 print '\txcodebodge.py -p <project> -t <target> add_source <file_name>'
1178 print '\txcodebodge.py -p <project> -t <target> remove_source <file_name>'
1179
1178 # List files in a native target 1180 # List files in a native target
1179 elif args[0] == 'list_target_sources': 1181 elif args[0] == 'list_target_sources':
1180 if len(args) != 1: 1182 if len(args) != 1:
1181 option_parser.error('list_target_sources takes no arguments') 1183 option_parser.error('list_target_sources takes no arguments')
1182 if not options.target: 1184 if not options.target:
1183 option_parser.error('list_target_sources requires a target') 1185 option_parser.error('list_target_sources requires a target')
1184 # Validate target and get list of files 1186 # Validate target and get list of files
1185 target = project.NativeTargetForName(options.target) 1187 target = project.NativeTargetForName(options.target)
1186 if not target: 1188 if not target:
1187 option_parser.error('No native target named "%s"' % options.target) 1189 option_parser.error('No native target named "%s"' % options.target)
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
1250 project.RelativeSourceRootPath(file_ref.abs_path) == source_path)) ): 1252 project.RelativeSourceRootPath(file_ref.abs_path) == source_path)) ):
1251 source_ref = file_ref 1253 source_ref = file_ref
1252 break 1254 break
1253 if not source_ref: 1255 if not source_ref:
1254 # Create a new source file ref 1256 # Create a new source file ref
1255 source_ref = project.AddSourceFile(os.path.abspath(source_path)) 1257 source_ref = project.AddSourceFile(os.path.abspath(source_path))
1256 # Add the new source file reference to the target if its a safe type 1258 # Add the new source file reference to the target if its a safe type
1257 if source_ref.file_type in SOURCES_XCODE_FILETYPES: 1259 if source_ref.file_type in SOURCES_XCODE_FILETYPES:
1258 project.AddSourceFileToSourcesBuildPhase(source_ref, sources_phase) 1260 project.AddSourceFileToSourcesBuildPhase(source_ref, sources_phase)
1259 project.Update() 1261 project.Update()
1260 1262
1261 # Private sanity check. On an unmodified project make sure our output is 1263 # Private sanity check. On an unmodified project make sure our output is
1262 # the same as the input 1264 # the same as the input
1263 elif args[0] == 'parse_sanity': 1265 elif args[0] == 'parse_sanity':
1264 if ''.join(project.FileContent()) != ''.join(project._raw_content): 1266 if ''.join(project.FileContent()) != ''.join(project._raw_content):
1265 option_parser.error('Project rewrite sanity fail "%s"' % project.path) 1267 option_parser.error('Project rewrite sanity fail "%s"' % project.path)
1266 1268
1267 else: 1269 else:
1268 Usage(option_parser) 1270 Usage(option_parser)
1269 1271
1270 1272
1271 if __name__ == '__main__': 1273 if __name__ == '__main__':
1272 Main() 1274 Main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698