Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add ttmodule script.

Implemented a pbxproj python script for adding dependencies to projects
via the command line.

It's built upon a collection of incredibly simple regular expressions
and a basic understanding of the pbxproj file format.

For help: python ttmodule.py -h
  • Loading branch information...
commit 0814b1c149cbe987557c88a271f999c3f9f3ae64 1 parent fbb1eda
Jeff Verkoeyen jverkoey authored
25 src/scripts/Paths.py
View
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+Paths.py
+
+Created by Jeff Verkoeyen on 2010-10-18.
+Copyright 2009-2010 Facebook
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import os
+
+script_dir = os.path.dirname(os.path.realpath(__file__))
+src_dir = os.path.dirname(script_dir)
161 src/scripts/Pbxproj.html
View
@@ -0,0 +1,161 @@
+
+<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head><title>Python: module Pbxproj</title>
+</head><body bgcolor="#f0f0f8">
+
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
+<tr bgcolor="#7799ee">
+<td valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>Pbxproj</strong></big></big></font></td
+><td align=right valign=bottom
+><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/Users/featherless/workbench/ios/three20/src/scripts/Pbxproj.py">/Users/featherless/workbench/ios/three20/src/scripts/Pbxproj.py</a></font></td></tr></table>
+ <p><tt><a href="#Pbxproj">Pbxproj</a>.py<br>
+&nbsp;<br>
+Working&nbsp;with&nbsp;the&nbsp;pbxproj&nbsp;file&nbsp;format&nbsp;is&nbsp;a&nbsp;pain&nbsp;in&nbsp;the&nbsp;ass.<br>
+&nbsp;<br>
+This&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;provides&nbsp;a&nbsp;couple&nbsp;basic&nbsp;features&nbsp;for&nbsp;parsing&nbsp;pbxproj&nbsp;files:<br>
+&nbsp;<br>
+*&nbsp;Getting&nbsp;a&nbsp;dependency&nbsp;list<br>
+*&nbsp;Adding&nbsp;one&nbsp;pbxproj&nbsp;to&nbsp;another&nbsp;pbxproj&nbsp;as&nbsp;a&nbsp;dependency<br>
+&nbsp;<br>
+Version&nbsp;1.0.<br>
+&nbsp;<br>
+History:<br>
+1.0&nbsp;-&nbsp;October&nbsp;20,&nbsp;2010:&nbsp;Initial&nbsp;hacked-together&nbsp;version&nbsp;finished.&nbsp;It&nbsp;is&nbsp;alive!<br>
+&nbsp;<br>
+Created&nbsp;by&nbsp;Jeff&nbsp;Verkoeyen&nbsp;on&nbsp;2010-10-18.<br>
+Copyright&nbsp;2009-2010&nbsp;Facebook<br>
+&nbsp;<br>
+Licensed&nbsp;under&nbsp;the&nbsp;Apache&nbsp;License,&nbsp;Version&nbsp;2.0&nbsp;(the&nbsp;"License");<br>
+you&nbsp;may&nbsp;not&nbsp;use&nbsp;this&nbsp;file&nbsp;except&nbsp;in&nbsp;compliance&nbsp;with&nbsp;the&nbsp;License.<br>
+You&nbsp;may&nbsp;obtain&nbsp;a&nbsp;copy&nbsp;of&nbsp;the&nbsp;License&nbsp;at<br>
+&nbsp;<br>
+&nbsp;&nbsp;&nbsp;<a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a><br>
+&nbsp;<br>
+Unless&nbsp;required&nbsp;by&nbsp;applicable&nbsp;law&nbsp;or&nbsp;agreed&nbsp;to&nbsp;in&nbsp;writing,&nbsp;software<br>
+distributed&nbsp;under&nbsp;the&nbsp;License&nbsp;is&nbsp;distributed&nbsp;on&nbsp;an&nbsp;"AS&nbsp;IS"&nbsp;BASIS,<br>
+WITHOUT&nbsp;WARRANTIES&nbsp;OR&nbsp;CONDITIONS&nbsp;OF&nbsp;ANY&nbsp;KIND,&nbsp;either&nbsp;express&nbsp;or&nbsp;implied.<br>
+See&nbsp;the&nbsp;License&nbsp;for&nbsp;the&nbsp;specific&nbsp;language&nbsp;governing&nbsp;permissions&nbsp;and<br>
+limitations&nbsp;under&nbsp;the&nbsp;License.</tt></p>
+<p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#aa55cc">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
+
+<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="Paths.html">Paths</a><br>
+<a href="hashlib.html">hashlib</a><br>
+</td><td width="25%" valign=top><a href="logging.html">logging</a><br>
+<a href="os.html">os</a><br>
+</td><td width="25%" valign=top><a href="re.html">re</a><br>
+<a href="sys.html">sys</a><br>
+</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ee77aa">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
+
+<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl>
+<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
+</font></dt><dd>
+<dl>
+<dt><font face="helvetica, arial"><a href="Pbxproj.html#Pbxproj">Pbxproj</a>
+</font></dt></dl>
+</dd>
+</dl>
+ <p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#ffc8d8">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#000000" face="helvetica, arial"><a name="Pbxproj">class <strong>Pbxproj</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
+
+<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%">Methods defined here:<br>
+<dl><dt><a name="Pbxproj-__init__"><strong>__init__</strong></a>(self, name)</dt><dd><tt>#&nbsp;Valid&nbsp;names<br>
+#&nbsp;Three20<br>
+#&nbsp;Three20:Three20-Xcode3.2.5<br>
+#&nbsp;/path/to/project.xcodeproj/project.pbxproj</tt></dd></dl>
+
+<dl><dt><a name="Pbxproj-__str__"><strong>__str__</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Pbxproj-add_build_setting"><strong>add_build_setting</strong></a>(self, configuration, setting_name, value)</dt></dl>
+
+<dl><dt><a name="Pbxproj-add_buildfile"><strong>add_buildfile</strong></a>(self, name, file_ref_hash, default_guid)</dt><dd><tt>#&nbsp;Add&nbsp;a&nbsp;line&nbsp;to&nbsp;the&nbsp;PBXBuildFile&nbsp;section.<br>
+#<br>
+#&nbsp;&lt;default_guid&gt;&nbsp;/*&nbsp;&lt;name&gt;&nbsp;in&nbsp;Frameworks&nbsp;*/&nbsp;=&nbsp;{isa&nbsp;=&nbsp;PBXBuildFile;&nbsp;fileRef&nbsp;=&nbsp;&lt;file_ref_hash&gt;&nbsp;/*&nbsp;&lt;name&gt;&nbsp;*/;&nbsp;};<br>
+#<br>
+#&nbsp;Returns:&nbsp;&lt;default_guid&gt;&nbsp;if&nbsp;a&nbsp;line&nbsp;was&nbsp;added.<br>
+#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Otherwise,&nbsp;the&nbsp;existing&nbsp;guid&nbsp;is&nbsp;returned.</tt></dd></dl>
+
+<dl><dt><a name="Pbxproj-add_dependency"><strong>add_dependency</strong></a>(self, dep)</dt></dl>
+
+<dl><dt><a name="Pbxproj-add_file_to_frameworks"><strong>add_file_to_frameworks</strong></a>(self, name, guid)</dt><dd><tt>#&nbsp;Add&nbsp;a&nbsp;file&nbsp;to&nbsp;the&nbsp;Frameworks&nbsp;PBXGroup.<br>
+#<br>
+#&nbsp;&lt;guid&gt;&nbsp;/*&nbsp;&lt;name&gt;&nbsp;*/,</tt></dd></dl>
+
+<dl><dt><a name="Pbxproj-add_file_to_frameworks_phase"><strong>add_file_to_frameworks_phase</strong></a>(self, name, guid)</dt></dl>
+
+<dl><dt><a name="Pbxproj-add_filereference"><strong>add_filereference</strong></a>(self, name, file_type, default_guid, rel_path, source_tree)</dt><dd><tt>#&nbsp;Add&nbsp;a&nbsp;line&nbsp;to&nbsp;the&nbsp;PBXFileReference&nbsp;section.<br>
+#<br>
+#&nbsp;&lt;default_guid&gt;&nbsp;/*&nbsp;&lt;name&gt;&nbsp;*/&nbsp;=&nbsp;{isa&nbsp;=&nbsp;PBXFileReference;&nbsp;lastKnownFileType&nbsp;=&nbsp;"wrapper.&lt;file_type&gt;";&nbsp;name&nbsp;=&nbsp;&lt;name&gt;;&nbsp;path&nbsp;=&nbsp;&lt;rel_path&gt;;&nbsp;sourceTree&nbsp;=&nbsp;&lt;source_tree&gt;;&nbsp;};<br>
+#<br>
+#&nbsp;Returns:&nbsp;&lt;default_guid&gt;&nbsp;if&nbsp;a&nbsp;line&nbsp;was&nbsp;added.<br>
+#&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Otherwise,&nbsp;the&nbsp;existing&nbsp;guid&nbsp;is&nbsp;returned.</tt></dd></dl>
+
+<dl><dt><a name="Pbxproj-add_framework"><strong>add_framework</strong></a>(self, framework)</dt></dl>
+
+<dl><dt><a name="Pbxproj-add_header_search_path"><strong>add_header_search_path</strong></a>(self, configuration)</dt></dl>
+
+<dl><dt><a name="Pbxproj-dependencies"><strong>dependencies</strong></a>(self)</dt><dd><tt>#&nbsp;Get&nbsp;and&nbsp;cache&nbsp;the&nbsp;dependencies&nbsp;for&nbsp;this&nbsp;project.</tt></dd></dl>
+
+<dl><dt><a name="Pbxproj-get_hash_base"><strong>get_hash_base</strong></a>(self, uniquename)</dt></dl>
+
+<dl><dt><a name="Pbxproj-get_project_data"><strong>get_project_data</strong></a>(self)</dt><dd><tt>#&nbsp;Load&nbsp;the&nbsp;project&nbsp;data&nbsp;from&nbsp;disk.</tt></dd></dl>
+
+<dl><dt><a name="Pbxproj-guid"><strong>guid</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Pbxproj-path"><strong>path</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Pbxproj-set_project_data"><strong>set_project_data</strong></a>(self, project_data)</dt><dd><tt>#&nbsp;Write&nbsp;the&nbsp;project&nbsp;data&nbsp;to&nbsp;disk.</tt></dd></dl>
+
+<dl><dt><a name="Pbxproj-uniqueid"><strong>uniqueid</strong></a>(self)</dt></dl>
+
+<dl><dt><a name="Pbxproj-xcodeprojpath"><strong>xcodeprojpath</strong></a>(self)</dt><dd><tt>#&nbsp;A&nbsp;pbxproj&nbsp;file&nbsp;is&nbsp;contained&nbsp;within&nbsp;an&nbsp;xcodeproj&nbsp;file.<br>
+#&nbsp;This&nbsp;method&nbsp;simply&nbsp;strips&nbsp;off&nbsp;the&nbsp;project.pbxproj&nbsp;part&nbsp;of&nbsp;the&nbsp;path.</tt></dd></dl>
+
+<hr>
+Static methods defined here:<br>
+<dl><dt><a name="Pbxproj-get_pbxproj_by_name"><strong>get_pbxproj_by_name</strong></a>(name)</dt></dl>
+
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+</td></tr></table></td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#eeaa77">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
+
+<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><dl><dt><a name="-commonpath"><strong>commonpath</strong></a>(l1, l2, common<font color="#909090">=[]</font>)</dt></dl>
+ <dl><dt><a name="-pathsplit"><strong>pathsplit</strong></a>(p, rest<font color="#909090">=[]</font>)</dt><dd><tt>#&nbsp;The&nbsp;following&nbsp;relative&nbsp;path&nbsp;methods&nbsp;recyled&nbsp;from:<br>
+#&nbsp;<a href="http://code.activestate.com/recipes/208993-compute-relative-path-from-one-directory-to-anothe/">http://code.activestate.com/recipes/208993-compute-relative-path-from-one-directory-to-anothe/</a><br>
+#&nbsp;Author:&nbsp;Cimarron&nbsp;Taylor<br>
+#&nbsp;Date:&nbsp;July&nbsp;6,&nbsp;2003</tt></dd></dl>
+ <dl><dt><a name="-relpath"><strong>relpath</strong></a>(p1, p2)</dt></dl>
+</td></tr></table><p>
+<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
+<tr bgcolor="#55aa55">
+<td colspan=3 valign=bottom>&nbsp;<br>
+<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
+
+<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
+<td width="100%"><strong>pbxproj_cache</strong> = {}</td></tr></table>
+</body></html>
700 src/scripts/Pbxproj.py
View
@@ -0,0 +1,700 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+Pbxproj.py
+
+Working with the pbxproj file format is a pain in the ass.
+
+This object provides a couple basic features for parsing pbxproj files:
+
+* Getting a dependency list
+* Adding one pbxproj to another pbxproj as a dependency
+
+Version 1.0.
+
+History:
+1.0 - October 20, 2010: Initial hacked-together version finished. It is alive!
+
+Created by Jeff Verkoeyen on 2010-10-18.
+Copyright 2009-2010 Facebook
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import hashlib
+import logging
+import os
+import re
+import sys
+import Paths
+
+pbxproj_cache = {}
+
+# The following relative path methods recyled from:
+# http://code.activestate.com/recipes/208993-compute-relative-path-from-one-directory-to-anothe/
+# Author: Cimarron Taylor
+# Date: July 6, 2003
+def pathsplit(p, rest=[]):
+ (h,t) = os.path.split(p)
+ if len(h) < 1: return [t]+rest
+ if len(t) < 1: return [h]+rest
+ return pathsplit(h,[t]+rest)
+
+def commonpath(l1, l2, common=[]):
+ if len(l1) < 1: return (common, l1, l2)
+ if len(l2) < 1: return (common, l1, l2)
+ if l1[0] != l2[0]: return (common, l1, l2)
+ return commonpath(l1[1:], l2[1:], common+[l1[0]])
+
+def relpath(p1, p2):
+ (common,l1,l2) = commonpath(pathsplit(p1), pathsplit(p2))
+ p = []
+ if len(l1) > 0:
+ p = [ '../' * len(l1) ]
+ p = p + l2
+ return os.path.join( *p )
+
+class Pbxproj(object):
+
+ @staticmethod
+ def get_pbxproj_by_name(name):
+ if name not in pbxproj_cache:
+ pbxproj_cache[name] = Pbxproj(name)
+
+ return pbxproj_cache[name]
+
+ # Valid names
+ # Three20
+ # Three20:Three20-Xcode3.2.5
+ # /path/to/project.xcodeproj/project.pbxproj
+ def __init__(self, name):
+ self._project_data = None
+
+ parts = name.split(':')
+ self.name = parts[0]
+ if len(parts) > 1:
+ self.target = parts[1]
+ else:
+ if re.match('^[a-zA-Z0-9\.\-:+"]+$', self.name):
+ self.target = self.name
+ else:
+ result = re.search('([a-zA-Z0-9\.\-+"]+)\.xcodeproj', self.name)
+ if not result:
+ self.target = self.name
+ else:
+ (self.target, ) = result.groups()
+
+ match = re.search('([a-zA-Z0-9\.\-+"]+)\.xcodeproj', self.name)
+ if not match:
+ self._project_name = self.name
+ else:
+ (self._project_name, ) = match.groups()
+
+ self._guid = None
+ self._deps = None
+ self.guid()
+
+ def __str__(self):
+ return str(self.name)+" target:"+str(self.target)+" guid:"+str(self._guid)+" prodguid: "+self._product_guid+" prodname: "+self._product_name
+
+ def uniqueid(self):
+ return self.name + ':' + self.target
+
+ def path(self):
+ # TODO: No sense calculating this every time, just store it when we get the name.
+ if re.match('^[a-zA-Z0-9\.\-:+"]+$', self.name):
+ return os.path.join(Paths.src_dir, self.name.strip('"'), self.name.strip('"')+'.xcodeproj', 'project.pbxproj')
+ elif not re.match('project.pbxproj$', self.name):
+ return os.path.join(self.name, 'project.pbxproj')
+ else:
+ return self.name
+
+ # A pbxproj file is contained within an xcodeproj file.
+ # This method simply strips off the project.pbxproj part of the path.
+ def xcodeprojpath(self):
+ return os.path.dirname(self.path())
+
+ def guid(self):
+ if not self._guid:
+ self.dependencies()
+
+ return self._guid
+
+ # Load the project data from disk.
+ def get_project_data(self):
+ if self._project_data is None:
+ if not os.path.exists(self.path()):
+ logging.info("Couldn't find the project at this path:")
+ logging.info(self.path())
+ return None
+
+ project_file = open(self.path(), 'r')
+ self._project_data = project_file.read()
+
+ return self._project_data
+
+ # Write the project data to disk.
+ def set_project_data(self, project_data):
+ if self._project_data != project_data:
+ self._project_data = project_data
+ project_file = open(self.path(), 'w')
+ project_file.write(self._project_data)
+
+ # Get and cache the dependencies for this project.
+ def dependencies(self):
+ if self._deps is not None:
+ return self._deps
+
+ project_data = self.get_project_data()
+
+ if project_data is None:
+ return None
+
+ result = re.search('([A-Z0-9]+) \/\* '+re.escape(self.target)+' \*\/ = {\n[ \t]+isa = PBXNativeTarget;(?:.|\n)+?([A-Z0-9]+) \/\* Frameworks \*\/,(?:.|\n)+?dependencies = \(\n((?:[ \t]+[A-Z0-9]+ \/\* PBXTargetDependency \*\/,\n)*)[ \t]*\);\n(?:.|\n)+?productReference = ([A-Z0-9]+) \/\* (.+?) \*\/;',
+ project_data)
+
+ if not result:
+ return None
+
+ (self._guid, self._frameworks_guid, dependency_set, self._product_guid, self._product_name, ) = result.groups()
+ dependency_guids = re.findall('[ \t]+([A-Z0-9]+) \/\* PBXTargetDependency \*\/,\n', dependency_set)
+
+ if not result:
+ return None
+
+ dependency_names = []
+
+ for guid in dependency_guids:
+ result = re.search(guid+' \/\* PBXTargetDependency \*\/ = \{\n[ \t]+isa = PBXTargetDependency;\n[ \t]*name = (["a-zA-Z0-9\.\-]+);',
+ project_data)
+
+ if result:
+ (dependency_name, ) = result.groups()
+ dependency_names.append(dependency_name)
+
+ self._deps = dependency_names
+
+ return self._deps
+
+ # Add a line to the PBXBuildFile section.
+ #
+ # <default_guid> /* <name> in Frameworks */ = {isa = PBXBuildFile; fileRef = <file_ref_hash> /* <name> */; };
+ #
+ # Returns: <default_guid> if a line was added.
+ # Otherwise, the existing guid is returned.
+ def add_buildfile(self, name, file_ref_hash, default_guid):
+ project_data = self.get_project_data()
+
+ match = re.search('\/\* Begin PBXBuildFile section \*\/\n((?:.|\n)+?)\/\* End PBXBuildFile section \*\/', project_data)
+
+ if not match:
+ logging.error("Couldn't find PBXBuildFile section.")
+ return None
+
+ (subtext, ) = match.groups()
+
+ buildfile_hash = None
+
+ match = re.search('([A-Z0-9]+).+?fileRef = '+re.escape(file_ref_hash), subtext)
+ if match:
+ (buildfile_hash, ) = match.groups()
+ logging.info("This build file already exists: "+buildfile_hash)
+
+ if buildfile_hash is None:
+ match = re.search('\/\* Begin PBXBuildFile section \*\/\n', project_data)
+
+ buildfile_hash = default_guid
+
+ libfiletext = "\t\t"+buildfile_hash+" /* "+name+" in Frameworks */ = {isa = PBXBuildFile; fileRef = "+file_ref_hash+" /* "+name+" */; };\n"
+ project_data = project_data[:match.end()] + libfiletext + project_data[match.end():]
+
+ self.set_project_data(project_data)
+
+ return buildfile_hash
+
+ # Add a line to the PBXFileReference section.
+ #
+ # <default_guid> /* <name> */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.<file_type>"; name = <name>; path = <rel_path>; sourceTree = <source_tree>; };
+ #
+ # Returns: <default_guid> if a line was added.
+ # Otherwise, the existing guid is returned.
+ def add_filereference(self, name, file_type, default_guid, rel_path, source_tree):
+ project_data = self.get_project_data()
+
+ fileref_hash = None
+
+ match = re.search('([A-Z0-9]+) \/\* '+re.escape(name)+' \*\/ = \{isa = PBXFileReference; lastKnownFileType = "wrapper.'+file_type+'"; name = '+re.escape(name)+'; path = '+re.escape(rel_path)+';', project_data)
+ if match:
+ logging.info("This file has already been added.")
+ (fileref_hash, ) = match.groups()
+
+ else:
+ match = re.search('\/\* Begin PBXFileReference section \*\/\n', project_data)
+
+ if not match:
+ logging.error("Couldn't find the PBXFileReference section.")
+ return False
+
+ fileref_hash = default_guid
+
+ pbxfileref = "\t\t"+fileref_hash+" /* "+name+" */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper."+file_type+"\"; name = "+name+"; path = "+rel_path+"; sourceTree = "+source_tree+"; };\n"
+ project_data = project_data[:match.end()] + pbxfileref + project_data[match.end():]
+
+ self.set_project_data(project_data)
+
+ return fileref_hash
+
+ # Add a file to the Frameworks PBXGroup.
+ #
+ # <guid> /* <name> */,
+ def add_file_to_frameworks(self, name, guid):
+ project_data = self.get_project_data()
+
+ match = re.search('\/\* Frameworks \*\/ = \{\n[ \t]+isa = PBXGroup;\n[ \t]+children = \(\n((?:.|\n)+?)\);', project_data)
+ if not match:
+ logging.error("Couldn't find the Frameworks children.")
+ return False
+
+ (children,) = match.groups()
+ match = re.search(re.escape(guid), children)
+ if match:
+ logging.info("This file is already a member of the Frameworks group.")
+ else:
+ match = re.search('\/\* Frameworks \*\/ = \{\n[ \t]+isa = PBXGroup;\n[ \t]+children = \(\n', project_data)
+
+ if not match:
+ logging.error("Couldn't find the Frameworks group.")
+ return False
+
+ pbxgroup = "\t\t\t\t"+guid+" /* "+name+" */,\n"
+ project_data = project_data[:match.end()] + pbxgroup + project_data[match.end():]
+
+ self.set_project_data(project_data)
+
+ return True
+
+ def add_file_to_frameworks_phase(self, name, guid):
+ project_data = self.get_project_data()
+
+ match = re.search(self._frameworks_guid+" \/\* Frameworks \*\/ = {(?:.|\n)+?files = \(((?:.|\n)+?)\);", project_data)
+
+ if not match:
+ logging.error("Couldn't find the frameworks.")
+ return False
+
+ (files, ) = match.groups()
+
+ match = re.search(re.escape(guid), files)
+ if match:
+ logging.info("The framework has already been added.")
+ else:
+ match = re.search(self._frameworks_guid+" \/\* Frameworks \*\/ = {(?:.|\n)+?files = \(\n", project_data)
+ if not match:
+ logging.error("Couldn't find the framework files")
+ return False
+
+ frameworktext = "\t\t\t\t"+guid+" /* "+name+" in Frameworks */,\n"
+ project_data = project_data[:match.end()] + frameworktext + project_data[match.end():]
+
+ self.set_project_data(project_data)
+
+ return True
+
+ def add_header_search_path(self, configuration):
+ project_path = os.path.dirname(os.path.abspath(self.xcodeprojpath()))
+ build_path = os.path.join(os.path.join(os.path.join(os.path.dirname(Paths.src_dir), 'Build'), 'Products'), 'three20')
+ rel_path = relpath(project_path, build_path)
+
+ return self.add_build_setting(configuration, 'HEADER_SEARCH_PATHS', rel_path)
+
+ def add_build_setting(self, configuration, setting_name, value):
+ project_data = self.get_project_data()
+
+ match = re.search('\/\* '+configuration+' \*\/ = {\n[ \t]+isa = XCBuildConfiguration;\n[ \t]+buildSettings = \{\n((?:.|\n)+?)\};', project_data)
+ if not match:
+ print "Couldn't find this configuration."
+ return False
+
+ settings_start = match.start(1)
+ settings_end = match.end(1)
+
+ (build_settings, ) = match.groups()
+
+ match = re.search(re.escape(setting_name)+' = ((?:.|\n)+?);', build_settings)
+
+ if not match:
+ settingtext = '\t\t\t\t'+setting_name+' = "'+value+'";\n'
+
+ project_data = project_data[:settings_start] + settingtext + project_data[settings_start:]
+ else:
+ (search_paths,) = match.groups()
+ if re.search('\(\n', search_paths):
+ match = re.search(re.escape(value), search_paths)
+ if not match:
+ match = re.search(re.escape(setting_name)+' = \(\n', build_settings)
+
+ build_settings = build_settings[:match.end()] + '\t\t\t\t\t"'+value+'",\n' + build_settings[match.end():]
+ project_data = project_data[:settings_start] + build_settings + project_data[settings_end:]
+ else:
+ if search_paths != value:
+ existing_path = search_paths
+ path_set = '(\n\t\t\t\t\t"'+value+'",\n\t\t\t\t\t'+existing_path+'\n\t\t\t\t)'
+ build_settings = build_settings[:match.start(1)] + path_set + build_settings[match.end(1):]
+ project_data = project_data[:settings_start] + build_settings + project_data[settings_end:]
+
+ self.set_project_data(project_data)
+
+ return True
+
+ def get_hash_base(self, uniquename):
+ examplehash = '320FFFEEEDDDCCCBBBAAA000'
+ uniquehash = hashlib.sha224(uniquename).hexdigest().upper()
+ uniquehash = uniquehash[:len(examplehash) - 4]
+ return '320'+uniquehash
+
+ def add_framework(self, framework):
+ tthash_base = self.get_hash_base(framework)
+
+ fileref_hash = self.add_filereference(framework, 'frameworks', tthash_base+'0', 'System/Library/Frameworks/'+framework, 'SDK_ROOT')
+ libfile_hash = self.add_buildfile(framework, fileref_hash, tthash_base+'1')
+ if not self.add_file_to_frameworks(framework, fileref_hash):
+ return False
+
+ if not self.add_file_to_frameworks_phase(framework, libfile_hash):
+ return False
+
+ return True
+
+ def add_dependency(self, dep):
+ project_data = self.get_project_data()
+ dep_data = dep.get_project_data()
+
+ if project_data is None or dep_data is None:
+ return False
+
+ logging.info("\nAdding "+str(dep)+" to "+str(self))
+
+ project_path = os.path.dirname(os.path.abspath(self.xcodeprojpath()))
+ dep_path = os.path.abspath(dep.xcodeprojpath())
+ rel_path = relpath(project_path, dep_path)
+
+ logging.info("")
+ logging.info("Project path: "+project_path)
+ logging.info("Dependency path: "+dep_path)
+ logging.info("Relative path: "+rel_path)
+
+ tthash_base = self.get_hash_base(dep.uniqueid())
+
+ ###############################################
+ logging.info("")
+ logging.info("Step 1: Add file reference to the dependency...")
+
+ self.set_project_data(project_data)
+ pbxfileref_hash = self.add_filereference(dep._project_name+'.xcodeproj', 'pb-project', tthash_base+'0', rel_path, 'SOURCE_ROOT')
+ project_data = self.get_project_data()
+
+ logging.info("Done: Added file reference: "+pbxfileref_hash)
+
+ ###############################################
+ logging.info("")
+ logging.info("Step 2: Add file to Frameworks group...")
+
+ self.set_project_data(project_data)
+ if not self.add_file_to_frameworks(dep._project_name+".xcodeproj", pbxfileref_hash):
+ return False
+ project_data = self.get_project_data()
+
+ logging.info("Done: Added file to Frameworks group.")
+
+ ###############################################
+ logging.info("")
+ logging.info("Step 3: Add dependencies...")
+
+ pbxtargetdependency_hash = None
+ pbxcontaineritemproxy_hash = None
+
+ match = re.search('\/\* Begin PBXTargetDependency section \*\/\n((?:.|\n)+?)\/\* End PBXTargetDependency section \*\/', project_data)
+ if not match:
+ logging.info("\tAdding a PBXTargetDependency section...")
+ match = re.search('\/\* End PBXSourcesBuildPhase section \*\/\n', project_data)
+
+ if not match:
+ logging.error("Couldn't find the PBXSourcesBuildPhase section.")
+ return False
+
+ project_data = project_data[:match.end()] + "\n/* Begin PBXTargetDependency section */\n\n/* End PBXTargetDependency section */\n" + project_data[match.end():]
+ else:
+ (subtext, ) = match.groups()
+ match = re.search('([A-Z0-9]+) \/\* PBXTargetDependency \*\/ = {\n[ \t]+isa = PBXTargetDependency;\n[ \t]+name = '+re.escape(dep._project_name)+';\n[ \t]+targetProxy = ([A-Z0-9]+) \/\* PBXContainerItemProxy \*\/;', project_data)
+ if match:
+ (pbxtargetdependency_hash, pbxcontaineritemproxy_hash,) = match.groups()
+ logging.info("This dependency already exists.")
+
+ if pbxtargetdependency_hash is None or pbxcontaineritemproxy_hash is None:
+ match = re.search('\/\* Begin PBXTargetDependency section \*\/\n', project_data)
+
+ pbxtargetdependency_hash = tthash_base+'1'
+ pbxcontaineritemproxy_hash = tthash_base+'2'
+
+ pbxtargetdependency = "\t\t"+pbxtargetdependency_hash+" /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tname = "+dep._project_name+";\n\t\t\ttargetProxy = "+pbxcontaineritemproxy_hash+" /* PBXContainerItemProxy */;\n\t\t};\n"
+ project_data = project_data[:match.end()] + pbxtargetdependency + project_data[match.end():]
+
+ logging.info("Done: Added dependency.")
+
+
+ ###############################################
+ logging.info("")
+ logging.info("Step 3.1: Add container proxy for dependencies...")
+
+ containerExists = False
+
+ match = re.search('\/\* Begin PBXContainerItemProxy section \*\/\n((?:.|\n)+?)\/\* End PBXContainerItemProxy section \*\/', project_data)
+ if not match:
+ logging.info("\tAdding a PBXContainerItemProxy section...")
+ match = re.search('\/\* End PBXBuildFile section \*\/\n', project_data)
+
+ if not match:
+ logging.error("Couldn't find the PBXBuildFile section.")
+ return False
+
+ project_data = project_data[:match.end()] + "\n/* Begin PBXContainerItemProxy section */\n\n/* End PBXContainerItemProxy section */\n" + project_data[match.end():]
+ else:
+ (subtext, ) = match.groups()
+ match = re.search(re.escape(pbxcontaineritemproxy_hash), subtext)
+ if match:
+ logging.info("This container proxy already exists.")
+ containerExists = True
+
+ if not containerExists:
+ match = re.search('\/\* Begin PBXContainerItemProxy section \*\/\n', project_data)
+
+ pbxcontaineritemproxy = "\t\t"+pbxcontaineritemproxy_hash+" /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = "+pbxfileref_hash+" /* "+dep._project_name+".xcodeproj */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = "+dep.guid()+";\n\t\t\tremoteInfo = "+dep._project_name+";\n\t\t};\n"
+ project_data = project_data[:match.end()] + pbxcontaineritemproxy + project_data[match.end():]
+
+ logging.info("Done: Added container proxy.")
+
+
+ ###############################################
+ logging.info("")
+ logging.info("Step 3.2: Add module to the dependency list...")
+
+ match = re.search(self.guid()+' \/\* .+? \*\/ = {\n[ \t]+(?:.|\n)+?[ \t]+dependencies = \(\n((?:.|\n)+?)\);', project_data)
+
+ dependency_exists = False
+
+ if not match:
+ logging.error("Couldn't find the dependency list.")
+ return False
+ else:
+ (dependencylist, ) = match.groups()
+ match = re.search(re.escape(pbxtargetdependency_hash), dependencylist)
+ if match:
+ logging.info("This dependency has already been added.")
+ dependency_exists = True
+
+ if not dependency_exists:
+ match = re.search(self.guid()+' \/\* .+? \*\/ = {\n[ \t]+(?:.|\n)+?[ \t]+dependencies = \(\n', project_data)
+
+ if not match:
+ logging.error("Couldn't find the dependency list.")
+ return False
+
+ dependency_item = '\t\t\t\t'+pbxtargetdependency_hash+' /* PBXTargetDependency */,\n'
+ project_data = project_data[:match.end()] + dependency_item + project_data[match.end():]
+
+ logging.info("Done: Added module to the dependency list.")
+
+
+ ###############################################
+ logging.info("")
+ logging.info("Step 4: Create project references...")
+
+ match = re.search('\/\* Begin PBXProject section \*\/\n((?:.|\n)+?)\/\* End PBXProject section \*\/', project_data)
+ if not match:
+ logging.error("Couldn't find the project section.")
+ return False
+
+ project_start = match.start(1)
+ project_end = match.end(1)
+
+ (project_section, ) = match.groups()
+
+ reference_exists = False
+ did_change = False
+
+ productgroup_hash = None
+
+ match = re.search('projectReferences = \(\n((?:.|\n)+?)\n[ \t]+\);', project_section)
+ if not match:
+ logging.info("Creating project references...")
+ match = re.search('projectDirPath = ".*?";\n', project_section)
+ if not match:
+ logging.error("Couldn't find project references anchor.")
+ return False
+
+ did_change = True
+ project_section = project_section[:match.end()] + '\t\t\tprojectReferences = (\n\t\t\t);\n' + project_section[match.end():]
+
+ else:
+ (refs, ) = match.groups()
+
+ match = re.search('\{\n[ \t]+ProductGroup = ([A-Z0-9]+) \/\* Products \*\/;\n[ \t]+ProjectRef = '+re.escape(pbxfileref_hash), refs)
+ if match:
+ (productgroup_hash, ) = match.groups()
+ logging.info("This product group already exists: "+productgroup_hash)
+ reference_exists = True
+
+
+ if not reference_exists:
+ match = re.search('projectReferences = \(\n', project_section)
+
+ if not match:
+ logging.error("Missing the project references item.")
+ return False
+
+ productgroup_hash = tthash_base+'3'
+
+ reference_text = '\t\t\t\t{\n\t\t\t\t\tProductGroup = '+productgroup_hash+' /* Products */;\n\t\t\t\t\tProjectRef = '+pbxfileref_hash+' /* '+dep._project_name+'.xcodeproj */;\n\t\t\t\t},\n'
+ project_section = project_section[:match.end()] + reference_text + project_section[match.end():]
+ did_change = True
+
+ if did_change:
+ project_data = project_data[:project_start] + project_section + project_data[project_end:]
+
+ logging.info("Done: Created project reference.")
+
+ ###############################################
+ logging.info("")
+ logging.info("Step 4.1: Create product group...")
+
+ match = re.search('\/\* Begin PBXGroup section \*\/\n', project_data)
+ if not match:
+ logging.error("Couldn't find the group section.")
+ return False
+
+ group_start = match.end()
+
+ lib_hash = None
+
+ match = re.search(re.escape(productgroup_hash)+" \/\* Products \*\/ = \{\n[ \t]+isa = PBXGroup;\n[ \t]+children = \(\n((?:.|\n)+?)\);", project_data)
+ if match:
+ logging.info("This product group already exists.")
+ (children, ) = match.groups()
+ match = re.search('([A-Z0-9]+) \/\* '+dep._product_name+' \*\/', children)
+ if not match:
+ logging.error("No product found.")
+ return False
+ # TODO: Add this product.
+ else:
+ (lib_hash, ) = match.groups()
+
+ else:
+ lib_hash = tthash_base+'4'
+
+ productgrouptext = "\t\t"+productgroup_hash+" /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t"+lib_hash+" /* "+dep._product_name+" */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n"
+ project_data = project_data[:group_start] + productgrouptext + project_data[group_start:]
+
+ logging.info("Done: Created product group: "+lib_hash)
+
+
+
+ ###############################################
+ logging.info("")
+ logging.info("Step 4.2: Add container proxy for target product...")
+
+ containerExists = False
+
+ targetproduct_hash = tthash_base+'6'
+
+ match = re.search('\/\* Begin PBXContainerItemProxy section \*\/\n((?:.|\n)+?)\/\* End PBXContainerItemProxy section \*\/', project_data)
+ if not match:
+ logging.info("\tAdding a PBXContainerItemProxy section...")
+ match = re.search('\/\* End PBXBuildFile section \*\/\n', project_data)
+
+ if not match:
+ logging.error("Couldn't find the PBXBuildFile section.")
+ return False
+
+ project_data = project_data[:match.end()] + "\n/* Begin PBXContainerItemProxy section */\n\n/* End PBXContainerItemProxy section */\n" + project_data[match.end():]
+ else:
+ (subtext, ) = match.groups()
+ match = re.search(re.escape(targetproduct_hash), subtext)
+ if match:
+ logging.info("This container proxy already exists.")
+ containerExists = True
+
+ if not containerExists:
+ match = re.search('\/\* Begin PBXContainerItemProxy section \*\/\n', project_data)
+
+ pbxcontaineritemproxy = "\t\t"+targetproduct_hash+" /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = "+pbxfileref_hash+" /* "+dep._project_name+".xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = "+dep._product_guid+";\n\t\t\tremoteInfo = "+dep._project_name+";\n\t\t};\n"
+ project_data = project_data[:match.end()] + pbxcontaineritemproxy + project_data[match.end():]
+
+ logging.info("Done: Added target container proxy.")
+
+
+ ###############################################
+ logging.info("")
+ logging.info("Step 4.3: Create reference proxy...")
+
+ referenceExists = False
+
+ match = re.search('\/\* Begin PBXReferenceProxy section \*\/\n((?:.|\n)+?)\/\* End PBXReferenceProxy section \*\/', project_data)
+ if not match:
+ logging.info("\tAdding a PBXReferenceProxy section...")
+ match = re.search('\/\* End PBXProject section \*\/\n', project_data)
+
+ if not match:
+ logging.error("Couldn't find the PBXProject section.")
+ return False
+
+ project_data = project_data[:match.end()] + "\n/* Begin PBXReferenceProxy section */\n\n/* End PBXReferenceProxy section */\n" + project_data[match.end():]
+ else:
+ (subtext, ) = match.groups()
+ match = re.search(re.escape(lib_hash), subtext)
+ if match:
+ logging.info("This reference proxy already exists.")
+ referenceExists = True
+
+ if not referenceExists:
+ match = re.search('\/\* Begin PBXReferenceProxy section \*\/\n', project_data)
+
+ referenceproxytext = "\t\t"+lib_hash+" /* "+dep._product_name+" */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = archive.ar;\n\t\t\tpath = "+dep._product_name+";\n\t\t\tremoteRef = "+targetproduct_hash+" /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n"
+ project_data = project_data[:match.end()] + referenceproxytext + project_data[match.end():]
+
+ logging.info("Done: Created reference proxy.")
+
+
+ ###############################################
+ logging.info("")
+ logging.info("Step 5: Add target file...")
+
+ self.set_project_data(project_data)
+ libfile_hash = self.add_buildfile(dep._product_name, lib_hash, tthash_base+'5')
+ project_data = self.get_project_data()
+
+ logging.info("Done: Added target file.")
+
+
+ ###############################################
+ logging.info("")
+ logging.info("Step 6: Add frameworks...")
+
+ self.set_project_data(project_data)
+ self.add_file_to_frameworks_phase(dep._product_name, libfile_hash)
+ project_data = self.get_project_data()
+
+ logging.info("Done: Adding module.")
+
+ self.set_project_data(project_data)
+
+ return True
160 src/scripts/ttmodule.py
View
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+ttmodule.py
+
+Most of the documentation is found in Pbxproj.py.
+
+Created by Jeff Verkoeyen on 2010-10-18.
+Copyright 2009-2010 Facebook
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import logging
+import re
+import sys
+from optparse import OptionParser
+
+# Three20 Python Objects
+import Paths
+from Pbxproj import Pbxproj
+
+def print_dependencies(name):
+ pbxproj = Pbxproj.get_pbxproj_by_name(name)
+ print str(pbxproj)+"..."
+ if pbxproj.dependencies():
+ [sys.stdout.write("\t"+x+"\n") for x in pbxproj.dependencies()]
+
+def get_dependency_modules(dependency_names):
+ dependency_modules = {}
+ if not dependency_names:
+ return dependency_modules
+
+ for name in dependency_names:
+ project = Pbxproj.get_pbxproj_by_name(name)
+ dependency_modules[project.uniqueid()] = project
+
+ dependencies = project.dependencies()
+ if dependencies is None:
+ print "Failed to get dependencies; it's possible that the given target doesn't exist."
+ sys.exit(0)
+
+ submodules = get_dependency_modules(dependencies)
+ for guid, submodule in submodules.items():
+ dependency_modules[guid] = submodule
+
+ return dependency_modules
+
+def add_modules_to_project(module_names, project, configs):
+ logging.info(project)
+ logging.info("Checking dependencies...")
+ if project.dependencies() is None:
+ logging.error("Failed to get dependencies; it's possible that the given target doesn't exist.")
+ sys.exit(0)
+ if len(project.dependencies()) == 0:
+ logging.info("\tNo dependencies.")
+ else:
+ logging.info("Existing dependencies:")
+ [logging.info("\t"+x) for x in project.dependencies()]
+
+ modules = get_dependency_modules(module_names)
+
+ logging.info("Requested dependency list:")
+ [logging.info("\t"+str(x)) for k,x in modules.items()]
+
+ logging.info("Adding dependencies...")
+ failed = []
+ for k,v in modules.items():
+ if v.name == 'Three20UI':
+ project.add_framework('QuartzCore.framework')
+
+ if not project.add_dependency(v):
+ failed.append(k)
+
+ if configs:
+ for config in configs:
+ project.add_header_search_path(config)
+ project.add_build_setting(config, 'OTHER_LDFLAGS', '-ObjC')
+ project.add_build_setting(config, 'OTHER_LDFLAGS', '-all_load')
+
+ if len(failed) > 0:
+ logging.error("Some dependencies failed to be added:")
+ [logging.error("\t"+str(x)+"\n") for x in failed]
+
+def main():
+ usage = '''%prog [options] module(s)
+
+The Three20 Module Script.
+Easily add Three20 modules to your projects.
+
+Modules may take the form <module-name>(:<module-target>)
+
+module-target defaults to module-name if it is not specified
+module-name may be a path to a .pbxproj file.
+
+Examples:
+ Print all dependencies for the Three20UI module
+ > %prog -d Three20UI
+
+ Print all dependencies for the Three20 module's Three20-Xcode3.2.5 target.
+ > %prog -d Three20:Three20-Xcode3.2.5
+
+ Add the Three20 project settings to the Debug and Release configurations.
+ This includes adding the header search path and linker flags.
+ > %prog -p path/to/myApp.xcodeproj -c Debug -c Release
+
+ Add the extThree20XML module and all of its dependencies to the myApp project.
+ > %prog -p path/to/myApp.xcodeproj extThree20XML
+
+ Add a specific target of a module to a project.
+ > %prog -p path/to/myApp.xcodeproj extThree20JSON:extThree20JSON+SBJSON'''
+ parser = OptionParser(usage = usage)
+ parser.add_option("-d", "--dependencies", dest="print_dependencies",
+ help="Print dependencies for the given modules",
+ action="store_true")
+ parser.add_option("-v", "--verbose", dest="verbose",
+ help="Display verbose output",
+ action="store_true")
+ parser.add_option("-p", "--project", dest="projects",
+ help="Add the given modules to this project", action="append")
+ parser.add_option("-c", "--config", dest="configs",
+ help="The configurations to add Three20 settings to (example: Debug)", action="append")
+
+ (options, args) = parser.parse_args()
+
+ if options.verbose:
+ log_level = logging.INFO
+ else:
+ log_level = logging.WARNING
+
+ logging.basicConfig(level=log_level)
+
+ did_anything = False
+
+ if options.print_dependencies:
+ [print_dependencies(x) for x in args]
+ did_anything = True
+
+ if options.projects is not None:
+ did_anything = True
+ for name in options.projects:
+ project = Pbxproj.get_pbxproj_by_name(name)
+ add_modules_to_project(args, project, options.configs)
+
+ if not did_anything:
+ parser.print_help()
+
+
+if __name__ == "__main__":
+ sys.exit(main())
Please sign in to comment.
Something went wrong with that request. Please try again.