def __setattr__(self, name, value): if name == u'pbx_buildConfigurationList': pbxhelper.pbxobj_set_pbxobj_attr(self, PBXProject, name, value, \ lambda o:isinstance(o, baseobject.PBXBaseObject) and o.isa == 'XCConfigurationList') elif name in [u'pbx_mainGroup', u'pbx_productRefGroup']: pbxhelper.pbxobj_set_pbxobj_attr(self, PBXProject, name, value, \ lambda o:isinstance(o, baseobject.PBXBaseObject) and o.isa == 'PBXGroup') elif name == u'pbx_targets': pbxhelper.pbxobj_set_pbxlist_attr(self, PBXProject, name, value, self.is_valid_target) elif name == u'pbx_projectReferences': self.__set_project_references(value) elif name == u'pbx_attributes': if not func.isdict(value): value = dict() super(PBXProject, self).__setattr__(name, value) elif name == u'pbx_knownRegions': if not func.isseq(value): value = [value] super(PBXProject, self).__setattr__(name, value) else: super(PBXProject, self).__setattr__(name, value)
def get_object(self, guid): """ get pbx-object with specified guid :param guid: the object's guid """ obj = self.__objects.get(guid) if obj is None and not self.__plist_objects is None: # self.__plist_objects is None indicates that the parsing process is finished objdict = self.__plist_objects.pop(guid, None) if func.isdict(objdict): objcls = objdict.pop(u'isa', None) if not objcls is None: try: obj = self.__new_object(objcls, guid) obj.parse(objdict) except Exception as e: logger.warn(e) raise else: logger.warn(u'[XcodeProj] Bad format. "isa" not found for object:{guid}'\ .format(guid=guid)) elif not objdict is None: logger.warn(\ u'[XcodeProj] {guid} invalid object dict:{dic}'.format(guid=guid, dic=objdict)) return obj
def __dispatch(obj): if func.isseq(obj): return __canonical_list(list(obj)) elif func.isdict(obj): return __canonical_dict(obj) else: return __canonical_obj(obj)
def __recursively_check_and_replace(obj, keys, oldval, newval): if isinstance(obj, PBXBaseObject): __recursively_check_and_replace(obj.__dict__, keys, oldval, newval) elif func.isseq(obj): __replace_array_item(obj, keys, oldval, newval) elif func.isdict(obj): k = keys.pop(0) v = obj.get(k) if isinstance(v, PBXBaseObject) and v.guid == oldval.guid: if newval is None: obj.pop(k, None) else: obj[k] = newval newval.add_referrer(self, keypath) v.remove_referrer(self) elif func.isdict(v): assert len(keys) > 0 __recursively_check_and_replace(v, keys, oldval, newval) elif func.isseq(v): __replace_array_item(v, keys, oldval, newval)
def __parse_dict_attr_val(self, dic, pbxkey): for k, v in dic.items(): depkey = u'{0}.{1}'.format(pbxkey, k) if func.isstr(v) and pbxhelper.is_valid_guid(v): obj = self.project().get_object(v) if not obj is None: obj.add_referrer(self, depkey) dic[k] = obj else: dic[k] = v elif func.isseq(v): dic[k] = self.__parse_arr_attr_val(v, depkey) elif func.isdict(v): self.__parse_dict_attr_val(v, depkey)
def is_valid_project_reference(self, value): """ check if value is valid project reference """ if not func.isdict(value): return False obj = value.get(u'ProductGroup') if not isinstance(obj, baseobject.PBXBaseObject) or not obj.isa == u'PBXGroup': return False obj = value.get(u'ProjectRef') if not isinstance(obj, baseobject.PBXBaseObject) or not obj.isa == u'PBXFileReference': return False return True
def __parse(self, plist_dict): objects = plist_dict.pop(u'objects', None) self.__plist_objects = objects if func.isdict(objects) else None for k, v in plist_dict.items(): plist_dict.pop(k) pbxkey = u'pbx_{name}'.format(name=k) if k == u'rootObject': v = self.get_object(v) setattr(self, pbxkey, v) if len(self.__plist_objects) > 0: logger.warn(u'[XcodeProj] isolate objects are not be parsed:\n\t{0}'\ .format(u'\n\t'.join([u'{0}:{1}'.format(k, v.get(u'isa')) \ for k, v in self.__plist_objects.items()]))) self.__plist_objects = None # process complete
def parse(self, objdict): """ parse object attribute values from 'objdict' """ objdict.pop(u'isa', None) for key in objdict.keys(): value = objdict.pop(key) pbxkey = u'pbx_{name}'.format(name=key) if func.isseq(value): newarr = self.__parse_arr_attr_val(value, pbxkey) setattr(self, pbxkey, newarr) elif func.isdict(value): self.__parse_dict_attr_val(value, pbxkey) setattr(self, pbxkey, value) else: self.__parse_str_attr(pbxkey, value)
def _print_value(self, buff, val, identstr, singleline=False): from xcodeproj.pbxproj import baseobject # self.safely_write(buff, u'') if func.isdict(val): pairs = sorted(val.items(), key=lambda e: e) self._print_pairs(buff, pairs, identstr, singleline) elif func.isseq(val): self._print_list(buff, val, identstr, singleline) elif isinstance(val, baseobject.PBXBaseObject): self.safely_write(buff, pbxhelper.pbxstr_escape(val.guid)) comment = val.comment() if not comment is None: self.safely_write(buff, u' /* {comment} */'.format(comment=comment)) else: self.safely_write(buff, pbxhelper.pbxstr_escape(val))
def load(xcprojpath, pbxprojfile=u'project.pbxproj'): """ load and parse pbxproj file. :param xcprojpath: path to ".xcodeproj". :param pbxprojfile: default is 'project.pbxproj', you can specified another name if needed """ xcprojpath = os.path.normpath(os.path.abspath(xcprojpath)) projname, projext = os.path.splitext(os.path.basename(xcprojpath)) if not projext == u'.xcodeproj' or not os.path.isdir(xcprojpath): logger.error(u'[XcodeProj] Illegal Xcode Project path: "%s"' % xcprojpath) sys.exit(1) pbxproj_path = os.path.join(xcprojpath, pbxprojfile) if not os.path.isfile(pbxproj_path): logger.error(u'[XcodeProj] Illegal Project file: "%s"' % pbxproj_path) sys.exit(1) import subprocess cmd_args = (u'/usr/bin/plutil', u'-convert', u'json', u'-o', u'-', pbxproj_path) p = subprocess.Popen(cmd_args, stdout=subprocess.PIPE) stdout, stderr = p.communicate() if not p.returncode == 0: logger.error(u'[XcodeProj] Incomprehensible file: %s; %s' % (pbxproj_path, stderr)) sys.exit(1) import json plist_dict = json.loads(stdout) if not func.isdict(plist_dict): logger.error(u'[XcodeProj] Bad format: %s; %s' % (pbxproj_path, stderr)) sys.exit(1) # parse objects xcproj = XcodeProj() xcproj.__project_file_path = xcprojpath xcproj.__pbxfile = pbxprojfile xcproj.__parse(plist_dict) return xcproj
def __parse_arr_attr_val(self, arr, pbxkey): newarr = [] for val in arr: if func.isstr(val) and pbxhelper.is_valid_guid(val): obj = self.project().get_object(val) if not obj is None: obj.add_referrer(self, pbxkey) newarr.append(obj) else: newarr.append(val) # error? elif func.isseq(val): newarr.append(self.__parse_arr_attr_val(val, pbxkey)) elif func.isdict(val): self.__parse_dict_attr_val(val, pbxkey) newarr.append(val) else: newarr.append(val) return newarr
def _validate(self): resolved, issues = super(PBXProject, self)._validate() pbxhelper.pbxobj_validate_pbxobj_attr(self, u'pbx_buildConfigurationList', issues=issues) pbxhelper.pbxobj_validate_pbxobj_attr(self, u'pbx_mainGroup', issues=issues) pbxhelper.pbxobj_validate_pbxobj_attr(self, u'pbx_productRefGroup', issues=issues) pbxhelper.pbxobj_validate_pbxlist_attr(self, u'pbx_targets', self.is_valid_target, \ resolved=resolved, issues=issues) dic = self.pbx_attributes.get(u'TargetAttributes') if not dic is None: if not func.isdict(dic): issues.append(u'invalid attributes.TargetAttributes: {0}'.format(dic)) else: for k, v in dic.items(): if not self.hastarget(k): dic.pop(k, None) resolved.append(\ u'remove attribute for dangling target:{guid}'.format(guid=k)) self.__validate_project_references(resolved, issues) self.__deduplicate_project_reference(resolved, issues) return resolved, issues
def __set_build_settings(self, value): if func.isdict(value): super(XCBuildConfiguration, self).__setattr__(u'pbx_buildSettings', value) else: logger.error(u'{0} illegal buildSettings:{1}'.format(self, value))
def __set_settings(self, value): if value is None or func.isdict(value): super(PBXBuildFile, self).__setattr__(u'pbx_settings', value) else: logger.error(u'[PBXBuildFile] illegal settings:{0}'.format(obj))