# 為什麼這麼做?
現在有這麼一個使用場景,基線能生成專案A,專案B,專案C...如果只有專案A中使用SDK_A,其他專案都不使用,這時候就需要對基線進行差分,只有當我切換到專案A時,才插入SDK_A。
不同於cocoapods的庫管理方式,xcodeproj是通過指令碼在編譯前向專案中插入指定程式碼檔案。
# Xcode中的專案結構
專案中都會有個主target作為根節點,底下有很多group節點,這些group節點管理著三類檔案
1. 常規的 .h/.m 檔案
2. 資原始檔
3. 庫檔案,分為靜態庫.a檔案、動態庫.framework檔案、閹割版的動態庫embed franework檔案
## 如何引入
這裡涉及到xcodeproj的具體使用,貼一下官方文件:
#獲取專案 $project = Xcodeproj::Project.open($project_path); #獲取target,通常取第一個為專案的主target $target = $project.targets.first # 獲取外掛目錄的group,如果不存在則建立 $group_One = $project[$plugin_folder] || $project.main_group.find_subpath(File.join($plugin_folder), true); # 在目標目錄新建group目錄 $group = $group_One.find_subpath($folderName, true) $SDK_PATH = $group_One.real_path.to_s + "/" + $plugin_folder + "/" + $folderName # 判斷SDK_PATH目錄下是否存在第三方程式碼,不存在則退出 if !FileTest::exists?($SDK_PATH) puts "SDK file not found in #{$SDK_PATH}" exit 1 end
* project_path 是專案的路徑
* plugin_folder 是插入的目錄名稱,它是建立在專案目錄底下的
* folder_name 是插入的第三方庫的資料夾的名稱,它是建立在plugin_folder目錄底下的
注意,這裡的第三庫的程式碼檔案其實和專案的程式碼是放在一塊的,我們要做的只是將它和專案關聯起來,也就是在XXX.xcodeproj檔案中新增其引用。具體的關聯就是建立target底下的group組後 新增上述3類檔案的引用。
1. .h/.m 檔案新增引用
if filePath.to_s.end_with?(".h") then fileReference = aGroup.new_reference(filePath); # aTarget.source_build_phase.add_file_reference(fileReference, true) elsif filePath.to_s.end_with?(".m", ".mm", ".cpp") then fileReference = aGroup.new_reference(filePath); aTarget.source_build_phase.add_file_reference(fileReference, true)
需要注意的是.h檔案只需要 在group底下new一個reference,.m檔案需要將group底下的reference新增進source_build_phase
2. 資原始檔新增引用
if filePath.to_s.end_with?(".bundle",".plist" ,".xml",".png",".xib",".js",".html",".css",".strings") fileReference = aGroup.new_reference(filePath); aTarget.resources_build_phase.add_file_reference(fileReference, true)
根據資原始檔建立其group的reference後,需要將其新增進resources_build_phase
3. 庫檔案的引用
if filePath.to_s.end_with?(".framework" ,".a") fileReference = aGroup.new_reference(filePath); build_phase = aTarget.frameworks_build_phase; build_phase.add_file_reference(fileReference); if $isEmbed == true #新增動態庫 $embed_framework.add_file_reference(fileReference) #勾上code sign on copy選項(預設是沒勾上的) $embed_framework.files.each do |file| # puts "entry filePath : #{filePath} fileRef path : #{file.file_ref.path}" if filePath.end_with?(file.file_ref.path) then if file.settings.nil? then # puts "setting is nil" file.settings = Hash.new end file.settings["ATTRIBUTES"] = ["CodeSignOnCopy", "RemoveHeadersOnCopy"] end end end
庫檔案需將 reference 新增進frameworks_build_phase,如果是embed framework還需將其新增進embed_frameworks_build_phase,其在xcodeproj中的具體型別是PBXCopyFilesBuildPhase
## 如何清除
因為SDK_A僅僅是專案A使用,如果從專案A切換到專案B,此時就得從XXX.xcodeproj檔案中清除關於SDK_A的所有引用,其實是新增引用的一個逆向過程。
上文中我們說到 將SDK_A插入{$PROJECT_PATH}/plugin_folder,所以只需遍歷這個group,清除其中所有的reference即可。
def removeBuildPhaseFilesRecursively(aTarget, aGroup) aGroup.files.each do |file| if file.real_path.to_s.end_with?(".m", ".mm", ".cpp") then aTarget.source_build_phase.remove_file_reference(file) elsif file.real_path.to_s.end_with?(".bundle",".plist" ,".xml",".png",".xib",".js",".html",".css",".strings") then aTarget.resources_build_phase.remove_file_reference(file) elsif file.real_path.to_s.end_with?(".framework" ,".a") aTarget.frameworks_build_phase.remove_file_reference(file) # remove embed ref if $isEmbed && !$embed_framework.nil? $embed_framework.remove_file_reference(file) end end # extra r+emove file ref file.remove_from_project end aGroup.groups.each do |group| # puts "group path : #{group.path}" if group.path == "embed" $isEmbed = true end removeBuildPhaseFilesRecursively(aTarget, group) $isEmbed = false end end
* .m 檔案的引用由source_build_phase移除
* 資原始檔的引用由resources_build_phase移除
* 庫檔案的引用由frameworks_build_phase移除,其中embed的framework還需由embed_framework_build_phase額外移除一下
# 後記
xcodeproj 其實不光只是新增引用,xcode中的build_settings的所有選項幾乎都可以通過xcodeproj的指令碼控制,這裡先按下不表。
最後附一下 github的傳送門地址:
https://github.com/xuanyuelin/ManuallyInsertCodes