在iOS开发过程的当中不知道大家有没有遇到过这样的问题
- 文件的物理位置不对应
- 多余的未被工程引用的文件
这两个问题在工程变得比较大的时候,对工程的影响就比较让人难受了,于是我决定对工程进行整理。
因为我们的业务还是蛮复杂的,工程就特别大,手动查找和整理简直就是一场灾难,于是我决定使用脚本来帮助我完成这项虽然简单但是巨大的工程。
文件物理位置不对应的原因
说到这个问题,我们就需要提一下project.pbxproj文件了,对,就是那个每次合并冲突都让人头疼的文件。
project.pbxproj文件本质上是一种旧风格的plist,因为可读性不是很好,逐渐被弃用,但是苹果却一直沿用,在这个文件当中包含了Xcode工程的各项配置和参数。project.pbxproj文件采用UDID作为key索引来保证文件的唯一性,同时又在value当中指定其对应子节点的UDID值,这样就将一个树形结构的数据用列表形式表示了出来(UDID是根据硬件和时间戳生成的唯一标识,这里不再详细解释其过程),工程的虚拟文件目录就是这么来的。
下面是一个转换成标准plist的结构,我们可以看到它的引用方式
因此我们解决物理位置不对应的问题只需要读取project.pbxproj的虚拟目录,并检查当前目录下是否有该文件,如果没有,到其对应的物理路径下,将其剪切过来即可。
针对第二个问题,我们可以扫描工程的整个物理目录,将每个文件去虚拟目录当中查找,如果没有被引用,直接删除即可。
脚本的编写
我们使用python来完成这次的任务,为什么不用shell?因为bash 3不支持key-value结构,要升级到bash 4才可以,而python比较方便,就使用它了,推荐使用python 3,因为它简化了蛮多操作的,比如编码什么的,不推荐使用预装的python 2。
1.读取project.pbxproj
我们先需要使用以下命令将project.pbxproj转换成标准的plist,然后使用python库biplist
读取
value=`plutil -convert xml1 -o - project.pbxproj` && echo $value >mPlistPath
2.检查虚拟目录下对应的文件是否存在,并进行相应操作
3.扫描工程物理路径,检查文件是否被引用,并进行操作
脚本如下:
# !/usr/bin/python
# coding=utf-8
from biplist import *
import os
global rootObjects
global rootArr
global plistPath
####################变量设置区#################################
rootPath= "/Users/yzq/Desktop/Test/Test"
projectFilePath = "/Users/yzq/Desktop/Test/Test.xcodeproj"
plistPath = "/Users/yzq/Desktop/123.plist"
##############################################################
def checkfile(filePath,filekey):
global rootObjects
dic = rootObjects[filekey]
# print(filekey)
strPath = ""
if "path" in dic:
strPath = dic["path"]
else:
strPath = dic["name"]
path = filePath + "/" + strPath
if "children" in dic:
if len(dic["children"]) < 1:
print("空文件夹"+"------"+path)
else:
for item in dic["children"]:
checkfile(path,item)
else:
if os.path.exists(path) == False:
print("文件不存在"+path)
def startCheckFile(mRootPath):
global rootObjects
global plistPath
plist = readPlist(plistPath)
rootObjects = plist["objects"]
for dic in rootObjects.values():
if ("name" in dic) and dic["name"] == "dfc_v3":
rootArr = dic["children"]
break
print(rootArr)
for item in rootArr:
print("开始检查item"+item+"-------------------------------")
checkfile(mRootPath,item)
def checkFileIsInProject(mPlistObject, mFileName, mFilePath):
for item in mPlistObject.values():
if (("path" in item) and (item["path"] == mFileName)) or (("name" in item) and (item["name"] == mFileName)):
# print("发现文件引用"+mFileName)
return
print("未发现文件应用:"+mFileName)
print(mFilePath)
def checkProject(mPlistObject, mFilePath):
dirList = os.listdir(mFilePath)
for item in dirList:
itemPath = os.path.join(mFilePath,item)
if os.path.isfile(itemPath):
checkFileIsInProject(mPlistObject,item,itemPath)
elif os.path.isdir(itemPath):
checkProject(mPlistObject,itemPath)
else:
print("当前路径出错:"+itemPath)
def startCheckProject(mRootPath):
global plistPath
plist = readPlist(plistPath)
rootObjects = plist["objects"]
checkProject(rootObjects,mRootPath)
def getPlist(mProjectFilePath,mPlistPath):
command = "rm -rf "+mPlistPath
os.system(command)
command = "value=`plutil -convert xml1 -o - "+mProjectFilePath+"/project.pbxproj` && echo $value >"+mPlistPath
os.system(command)
print("生成plist文件:"+mPlistPath)
print("这是一个帮你检查工程文件的脚本,请输入以下数字选项执行")
print("0.准备工作")
print("1.检查工程列表当中的文件是否在对应的物理位置")
print("2.检查工程当中是否有文件未加入引用")
choice = input("请输入:")
if choice == "0":
print("请设置脚本rootPath、projectFilePath、plistPath三个变量的路径值")
print("rootPath:工程根目录,dfc_v2的那个文件夹目录")
print("projectFilePath:工程文件目录,dfc_v2.xcodeproj文件目录")
print("plistPath:生成plist文件所在位置,该位置任意,具备读写权限即可")
elif choice == "1":
getPlist(projectFilePath,plistPath)
startCheckFile(rootPath)
elif choice == "2":
getPlist(projectFilePath,plistPath)
startCheckProject(rootPath)
else:
print("未输入正确的操作")
因为贸然的使用脚本改动工程有一定的风险,所以我在脚本当中把有问题的文件及其目录输出,手动去改动。
执行脚本
执行脚本,让后根据输出的结果修改工程,因为Supporting Files
下的文件直接是在主目录下的,它下面的文件直接忽略即可。
python3 my.py