def fromFile(cls, filename, classPrefix, includeRestorationIDs=False): res = cls(lib.bareFilename(filename), classPrefix) root = ElementTree.parse(fn) segueIds = getAttrsForAllNodesWithAttr(root, 'identifier', 'segue') res._addIds(segueIds, cls.SEGUE) # This seems to be limited to view controllers, but we can't specify a tag, as different # UIViewController subclasses have different tags (e.g. nav controllers). viewControllerIds = getAttrsForAllNodesWithAttr( root, 'storyboardIdentifier') res._addIds(viewControllerIds, cls.VIEW_CONTROLLER) reuseIds = getAttrsForAllNodesWithAttr(root, 'reuseIdentifier') res._addIds(reuseIds, cls.REUSE) if includeRestorationIDs: restorationIds = getAttrsForAllNodesWithAttr( root, 'restorationIdentifier') restorationIds.extend( getRestorationIDsForVCsUsingStoryboardIDs(root)) res._addIds(restorationIds, cls.RESTORATION) return res
def getIconFilenames(dir): # We want the processed Info.plist in the .app, not the one in the project. # Xcode consolidates asset catalog info into that plist, so it's a reliable # source for the names of icon files, regardless of how they got to be that way. packagedInfoPlist, _ = lib.loadPlist(os.path.join(dir, "Info.plist")) iPhoneIcons = packagedInfoPlist.valueForKeyPath_("CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles") or [] iPadIcons = packagedInfoPlist.valueForKeyPath_("CFBundleIcons~ipad.CFBundlePrimaryIcon.CFBundleIconFiles") or [] allIconNames = set(itertools.chain(iPhoneIcons, iPadIcons)) resSuffixes = ["", "@2x", "@3x"] devSuffixes = ["", "~ipad"] fns = [ os.path.join(dir, fn + res + dev + ".png") for fn in allIconNames for res in resSuffixes for dev in devSuffixes ] return filter(os.path.exists, fns)
def _addId(self, id_, type_): targetDict = None suffixes = [ 'ID', 'Id', 'Identifier' ] if type_ == self.SEGUE: targetDict = self.segues suffixes.append('Segue') elif type_ == self.VIEW_CONTROLLER: targetDict = self.viewControllers suffixes.extend(['ViewController', 'Controller', 'VC']) elif type_ == self.REUSE: targetDict = self.reusables suffixes.append('Reuse') elif type_ == self.RESTORATION: targetDict = self.restorables suffixes.append('Restoration') variableName = lib.variableNameForString(id_, self._defaultPrefixes, suffixes) targetDict[variableName] = id_
def headerAndImpContentsForCatalog(catalogDir, classPrefix): imageNames = imageNamesInCatalog(catalogDir) if not imageNames: return ([], []) className = classNameForCatalog(catalogDir, classPrefix) hLines = ['@interface {}: NSObject\n'.format(className)] mLines = ['@implementation {}\n'.format(className)] for imageName in imageNames: identifier = lib.variableNameForString(imageName) hLines.append('+(UIImage *)' + identifier + ';') mLines.append('+(UIImage *)' + identifier + ' { return [UIImage imageNamed:@"' + imageName + '"]; }') hLines.append('\n@end') mLines.append('\n@end') return ('\n'.join(hLines), '\n'.join(mLines))
def _addId(self, id_, type_): targetDict = None suffixes = ['ID', 'Id', 'Identifier'] if type_ == self.SEGUE: targetDict = self.segues suffixes.append('Segue') elif type_ == self.VIEW_CONTROLLER: targetDict = self.viewControllers suffixes.extend(['ViewController', 'Controller', 'VC']) elif type_ == self.REUSE: targetDict = self.reusables suffixes.append('Reuse') elif type_ == self.RESTORATION: targetDict = self.restorables suffixes.append('Restoration') variableName = lib.variableNameForString(id_, self._defaultPrefixes, suffixes) targetDict[variableName] = id_
def getIconFilenames(dir): # We want the processed Info.plist in the .app, not the one in the project. # Xcode consolidates asset catalog info into that plist, so it's a reliable # source for the names of icon files, regardless of how they got to be that way. packagedInfoPlist, _ = lib.loadPlist(os.path.join(dir, "Info.plist")) iPhoneIcons = packagedInfoPlist.valueForKeyPath_("CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles") or [] iPadIcons = packagedInfoPlist.valueForKeyPath_("CFBundleIcons~ipad.CFBundlePrimaryIcon.CFBundleIconFiles") or [] fns = [] fns.extend([fn + ".png" for fn in iPhoneIcons]) fns.extend([fn + "@2x.png" for fn in iPhoneIcons]) fns.extend([fn + "@3x.png" for fn in iPhoneIcons]) # IT'S HAPPENING fns.extend([fn + "~ipad.png" for fn in iPadIcons]) fns.extend([fn + "@2x~ipad.png" for fn in iPadIcons]) fns.extend([fn + "@3x~ipad.png" for fn in iPadIcons]) fns = [os.path.join(dir, fn) for fn in fns] fns = filter(os.path.exists, fns) return fns
def __init__(self, storyboardName, classPrefix): self.name = storyboardName self.classPrefix = classPrefix self.segues = {} self.viewControllers = {} self.reusables = {} self.restorables = {} self._defaultPrefixes = [ classPrefix, storyboardName, 'Storyboard' ] if storyboardName.endswith('Storyboard'): self._defaultPrefixes.append(storyboardName[:-10]) self.className = lib.variableNameForString(storyboardName, [ classPrefix ], [ 'Storyboard' ], lower = False) self._defaultPrefixes.append(self.className) if classPrefix: self.className = classPrefix + self.className self._defaultPrefixes.append(self.className) self.className += 'StoryboardIDs'
def getIconFilenames(dir): # We want the processed Info.plist in the .app, not the one in the project. # Xcode consolidates asset catalog info into that plist, so it's a reliable # source for the names of icon files, regardless of how they got to be that way. packagedInfoPlist, _ = lib.loadPlist(os.path.join(dir, 'Info.plist')) iPhoneIcons = packagedInfoPlist.valueForKeyPath_( 'CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles') or [] iPadIcons = packagedInfoPlist.valueForKeyPath_( 'CFBundleIcons~ipad.CFBundlePrimaryIcon.CFBundleIconFiles') or [] allIconNames = set(itertools.chain(iPhoneIcons, iPadIcons)) resSuffixes = ['', '@2x', '@3x'] devSuffixes = ['', '~ipad'] fns = [ os.path.join(dir, fn + res + dev + '.png') for fn in allIconNames for res in resSuffixes for dev in devSuffixes ] return filter(os.path.exists, fns)
def fromFile(cls, filename, classPrefix, includeRestorationIDs = False): res = cls(lib.bareFilename(filename), classPrefix) root = ElementTree.parse(fn) segueIds = getAttrsForAllNodesWithAttr(root, 'identifier', 'segue') res._addIds(segueIds, cls.SEGUE) # This seems to be limited to view controllers, but we can't specify a tag, as different # UIViewController subclasses have different tags (e.g. nav controllers). viewControllerIds = getAttrsForAllNodesWithAttr(root, 'storyboardIdentifier') res._addIds(viewControllerIds, cls.VIEW_CONTROLLER) reuseIds = getAttrsForAllNodesWithAttr(root, 'reuseIdentifier') res._addIds(reuseIds, cls.REUSE) if includeRestorationIDs: restorationIds = getAttrsForAllNodesWithAttr(root, 'restorationIdentifier') restorationIds.extend(getRestorationIDsForVCsUsingStoryboardIDs(root)) res._addIds(restorationIds, cls.RESTORATION) return res
def __init__(self, storyboardName, classPrefix): self.name = storyboardName self.classPrefix = classPrefix self.segues = {} self.viewControllers = {} self.reusables = {} self.restorables = {} self._defaultPrefixes = [classPrefix, storyboardName, 'Storyboard'] if storyboardName.endswith('Storyboard'): self._defaultPrefixes.append(storyboardName[:-10]) self.className = lib.variableNameForString(storyboardName, [classPrefix], ['Storyboard'], lower=False) self._defaultPrefixes.append(self.className) if classPrefix: self.className = classPrefix + self.className self._defaultPrefixes.append(self.className) self.className += 'StoryboardIDs'
def imageNamesInCatalog(catalogDir): imagesetDirs = glob(os.path.join(catalogDir, '*.imageset')) return [lib.bareFilename(d) for d in imagesetDirs]
imp = warning imp += '#import "' + outputBasename + '.h"\n\n' imp += '\n\n'.join(lines[1]) headerFn = os.path.join(outputDir, outputBasename + '.h') impFn = os.path.join(outputDir, outputBasename + '.m') with open(headerFn, 'w') as f: f.write(header.encode('utf-8')) with open(impFn, 'w') as f: f.write(imp.encode('utf-8')) if __name__ == '__main__': outBasename = 'AssetCatalogIdentifiers' prefix = lib.classPrefix projectDir = os.path.join(lib.getEnv('SRCROOT'), lib.getEnv('PROJECT_NAME')) catalogDirs = list(lib.recursiveGlob(projectDir, '*.xcassets', includeDirs = True)) lines = ([], []) for catalogDir in catalogDirs: hString, mString = headerAndImpContentsForCatalog(catalogDir, prefix) if hString: lines[0].append(hString) lines[1].append(mString) outDir = os.path.join(projectDir, 'Other-Sources', 'Generated') assembleAndOutput(lines, outDir, outBasename) print 'Generated {}.h and .m for image assets in the following catalog(s): {}'.format(outBasename, ', '.join([os.path.basename(d) for d in catalogDirs]))
headerFn = os.path.join(outputDir, outputBasename + '.h') impFn = os.path.join(outputDir, outputBasename + '.m') with open(headerFn, 'w') as f: f.write(header.encode('utf-8')) with open(impFn, 'w') as f: f.write(imp.encode('utf-8')) if __name__ == '__main__': outBasename = 'StoryboardIdentifiers' prefix = lib.classPrefix needRestorationIDs = 'NEED_RESTORATION_IDS' in os.environ projectDir = os.path.join(lib.getEnv('SRCROOT'), lib.getEnv('PROJECT_NAME')) inputFiles = list(lib.recursiveGlob(projectDir, '*.storyboard')) lines = ([], []) for fn in inputFiles: idList = IDList.fromFile(fn, prefix, needRestorationIDs) hString, mString = idList.headerAndImpContents() if hString: lines[0].append('#pragma mark ' + idList.name) lines[0].append(hString) lines[1].append('#pragma mark ' + idList.name) lines[1].append(mString) outDir = os.path.join(projectDir, 'Other-Sources', 'Generated')
from __future__ import print_function, unicode_literals import AmaroLib as lib from sys import exit from glob import glob import os.path from shutil import rmtree # Don't run in continuous integration or if we're not a build for release if lib.inContinuousIntegration or not lib.isDistributionOrAdHocBuildForDevice: print('Not removing localizations; this is an internal build') exit(0) # The project object has a knownRegions property that is an array of # locale strings (e.g. "Base", "en") that were set up in Xcode. knownRegions = lib.getProjectKeypath('knownRegions') if not knownRegions: lib.warn('The project is reporting that there are no known localizations in your project.\nPlease report this at {}'.format(lib.REPORT_URL)) exit(0) print('Keeping localizations for regions: ' + ', '.join(knownRegions)) regionsAndFoldersToDelete = [] lprojFolders = glob(os.path.join(lib.getEnv('CODESIGNING_FOLDER_PATH'), '*.lproj')) for lprojFolder in lprojFolders: region = lib.bareFilename(lprojFolder) if not region in knownRegions: regionsAndFoldersToDelete.append((region, lprojFolder)) if regionsAndFoldersToDelete:
#!/usr/bin/env python2.7 # This script increments the build number (CFBundleVersion in the Info.plist) # on Ad-Hoc or Distribution builds that target real devices. # In the context of Amaro, that means an archive build of any scheme, or any # non-simulator build of the AppStore scheme. # This script should be run as a build phase before the "Copy Bundle Resources" # phase. It has no input or output files. from __future__ import print_function, unicode_literals import AmaroLib as lib from sys import exit # Bail unless we're a build for something that's going to be released if not lib.isDistributionOrAdHocBuildForDevice: print('Not incrementing build number; this is an internal build') exit(0) buildNumber = lib.buildNumber try: buildNumber = int(buildNumber) except ValueError, e: lib.warn('Unable to parse your build number ("{}") as an integer. Not incrementing it!'.format(buildNumber)) exit(0) lib.infoPlist['CFBundleVersionKey'] = str(buildNumber + 1) lib.writePlist(lib.infoPlistFilename, lib.infoPlist, lib.infoPlistFormat)
def classNameForCatalog(catalogDir, classPrefix): name = lib.bareFilename(catalogDir) return classPrefix + lib.variableNameForString(name, [classPrefix], ['AssetCatalog', 'Catalog'], lower = False) + 'Catalog'
headerFn = os.path.join(outputDir, outputBasename + '.h') impFn = os.path.join(outputDir, outputBasename + '.m') with open(headerFn, 'w') as f: f.write(header.encode('utf-8')) with open(impFn, 'w') as f: f.write(imp.encode('utf-8')) if __name__ == '__main__': outBasename = 'StoryboardIdentifiers' prefix = lib.classPrefix needRestorationIDs = 'NEED_RESTORATION_IDS' in os.environ projectDir = os.path.join(lib.getEnv('SRCROOT'), lib.getEnv('PROJECT_NAME')) inputFiles = list(lib.recursiveGlob(projectDir, '*.storyboard')) lines = ([], []) for fn in inputFiles: idList = IDList.fromFile(fn, prefix, needRestorationIDs) hString, mString = idList.headerAndImpContents() if hString: lines[0].append('#pragma mark ' + idList.name) lines[0].append(hString) lines[1].append('#pragma mark ' + idList.name) lines[1].append(mString)
#!/usr/bin/env python2.7 # This script increments the build number (CFBundleVersion in the Info.plist) # on Ad-Hoc or Distribution builds that target real devices. # In the context of Amaro, that means an archive build of any scheme, or any # non-simulator build of the AppStore scheme. # This script should be run as a build phase before the "Copy Bundle Resources" # phase. It has no input or output files. from __future__ import print_function, unicode_literals import AmaroLib as lib from sys import exit import subprocess # Bail unless we're a build for something that's going to be released if not lib.isDistributionOrAdHocBuildForDevice: print('Not incrementing build number; this is an internal build') exit(0) newBuildNumber = subprocess.check_output(["./bin/buildNo.sh",], shell=True).strip() print("build number: %s" % newBuildNumber) lib.infoPlist['CFBundleVersion'] = newBuildNumber lib.writePlist(lib.infoPlistFilename, lib.infoPlist, lib.infoPlistFormat)
# This script increments the build number (CFBundleVersion in the Info.plist) # on Ad-Hoc or Distribution builds that target real devices. # In the context of Amaro, that means an archive build of any scheme, or any # non-simulator build of the AppStore scheme. # This script should be run as a build phase before the "Copy Bundle Resources" # phase. It has no input or output files. from __future__ import print_function, unicode_literals import AmaroLib as lib from sys import exit # Bail unless we're a build for something that's going to be released if not lib.isDistributionOrAdHocBuildForDevice: print('Not incrementing build number; this is an internal build') exit(0) buildNumber = lib.buildNumber try: buildNumber = int(buildNumber) except ValueError, e: lib.warn( 'Unable to parse your build number ("{}") as an integer. Not incrementing it!' .format(buildNumber)) exit(0) newBuildNumber = str(buildNumber + 1) lib.infoPlist['CFBundleVersion'] = newBuildNumber print('Incrementing build number to {}'.format(newBuildNumber)) lib.writePlist(lib.infoPlistFilename, lib.infoPlist, lib.infoPlistFormat)
def classNameForCatalog(catalogDir, classPrefix): name = lib.bareFilename(catalogDir) return classPrefix + lib.variableNameForString(name, [classPrefix], ['AssetCatalog', 'Catalog'], lower=False) + 'Catalog'
headerFn = os.path.join(outputDir, outputBasename + '.h') impFn = os.path.join(outputDir, outputBasename + '.m') with open(headerFn, 'w') as f: f.write(header.encode('utf-8')) with open(impFn, 'w') as f: f.write(imp.encode('utf-8')) if __name__ == '__main__': outBasename = 'AssetCatalogIdentifiers' prefix = lib.classPrefix projectDir = os.path.join(lib.getEnv('SRCROOT'), lib.getEnv('PROJECT_NAME')) catalogDirs = list( lib.recursiveGlob(projectDir, '*.xcassets', includeDirs=True)) lines = ([], []) for catalogDir in catalogDirs: hString, mString = headerAndImpContentsForCatalog(catalogDir, prefix) if hString: lines[0].append(hString) lines[1].append(mString) outDir = os.path.join(projectDir, 'Other-Sources', 'Generated') assembleAndOutput(lines, outDir, outBasename) print(
def getIconFilenames(dir): # We want the processed Info.plist in the .app, not the one in the project. # Xcode consolidates asset catalog info into that plist, so it's a reliable # source for the names of icon files, regardless of how they got to be that way. packagedInfoPlist, _ = lib.loadPlist(os.path.join(dir, 'Info.plist')) iPhoneIcons = packagedInfoPlist.valueForKeyPath_('CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles') or [] iPadIcons = packagedInfoPlist.valueForKeyPath_('CFBundleIcons~ipad.CFBundlePrimaryIcon.CFBundleIconFiles') or [] allIconNames = set(itertools.chain(iPhoneIcons, iPadIcons)) resSuffixes = ['', '@2x', '@3x'] devSuffixes = ['', '~ipad'] fns = [os.path.join(dir, fn + res + dev + '.png') for fn in allIconNames for res in resSuffixes for dev in devSuffixes] return filter(os.path.exists, fns) if __name__ == '__main__': sourceDir = lib.getEnv('CODESIGNING_FOLDER_PATH') iconFns = getIconFilenames(sourceDir) for fn in iconFns: badgeFile(fn, sourceDir, lib.targetingStaging, lib.version, lib.buildNumber) print('Badged the following icon files: ' + ', '.join([os.path.basename(fn) for fn in iconFns]))
packagedInfoPlist, _ = lib.loadPlist(os.path.join(dir, 'Info.plist')) iPhoneIcons = packagedInfoPlist.valueForKeyPath_( 'CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles') or [] iPadIcons = packagedInfoPlist.valueForKeyPath_( 'CFBundleIcons~ipad.CFBundlePrimaryIcon.CFBundleIconFiles') or [] allIconNames = set(itertools.chain(iPhoneIcons, iPadIcons)) resSuffixes = ['', '@2x', '@3x'] devSuffixes = ['', '~ipad'] fns = [ os.path.join(dir, fn + res + dev + '.png') for fn in allIconNames for res in resSuffixes for dev in devSuffixes ] return filter(os.path.exists, fns) if __name__ == '__main__': sourceDir = lib.getEnv('CODESIGNING_FOLDER_PATH') iconFns = getIconFilenames(sourceDir) for fn in iconFns: badgeFile(fn, sourceDir, lib.targetingStaging, lib.version, lib.buildNumber) print('Badged the following icon files: ' + ', '.join([os.path.basename(fn) for fn in iconFns]))