本文梳理了如下一些问题
- 关于Cocoapods
- –verbose,–no-repo-update
- pod install –verbose的过程
- podfile 和 podspec
- 看点CocoaPods源码
- Podfile.lock 和 Manifest.lock
- pod install 和 update的区别
- 那些常见的目录&指令
- 期望用更简洁的话,做一些整理
关于Cocoapods
- 管理Xcode项目依赖库的工具
项目依赖被详细写在Podfile的文件中,CocoaPods解决libraries之间的依赖关系、获取目标库的源码,并将它链接到Xcode workspace中来构建项目。最终目标是建立一个查找效率高效、更规范的第三方开源库生态环境
–verbose,–no-repo-update
- pod install/update –no-repo-update:默认情况下install/update时,会默认更新一次podspec索引,使用–no-repo可以禁止对其索引的更新(这里说的就是local spec)
- pod install/update –verbose:verbose表示显示详细的冗长的过程,没什么特殊的功能性的意义,只是把详细的过程列出来给你看,更有助于了解底层运行
podfile 和 podspec
- 高度定制所需要的三方库
- 描述文件,描述pod库的版本。描述一个库怎么被添加到工程中,支持:列出源文件、依赖库、编译选项和其他第三方库需要的配置。
- 拿三方库举例,pod项目的podspec都托管在“https://github.com/CocoaPods/Specs”这个远程索引库
- pod setup时,CocoaPods会将这些podspec索引文件更新到本地”~/.cocoapods/”目录下
pod install –verbose
- Analyzing dependencies:依赖解析
- Inspecting targets to integrate:检查要集成的目标
- Resolving dependencies of
Podfile
:解析Podfile-版本控制和冲突:Milinillo依赖解决算法 - Comparing resolved specification to the sandbox manifest
- Downloading dependencies:下载依赖
- 如下,installing是会下载到~/Library/Caches/CocoaPods/Pods/Release/…下,然后copy到当前工程的Pods目录下
1
2
3
4
5
6
7-> Installing AFNetworking (2.5.4.1)
> Copying AFNetworking from
`/Users/username/Library/Caches/CocoaPods/Pods/Release/AFNetworking/2.5.4.1-313e5`
to `Pods/AFNetworking`
也可能已存在,就是using
-> Using AFNetworking (2.5.4.1)
- 如下,installing是会下载到~/Library/Caches/CocoaPods/Pods/Release/…下,然后copy到当前工程的Pods目录下
- Generating Pods project
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Generating Pods project
- Creating Pods project
- Adding source files to Pods project
- Adding frameworks to Pods project
- Adding libraries to Pods project
- Adding resources to Pods project
- Linking headers
- Installing targets
- Installing target `AFNetworking` iOS 7.0
- Installing target `Pods-testTaggedPointer` iOS 8.0
- Running post install hooks
- Writing Xcode project file to `Pods/Pods.xcodeproj`
- Generating deterministic UUIDs
- Writing Lockfile in `Podfile.lock`
- Writing Manifest in `Pods/Manifest.lock`
主要做了:
- 生成Pods.xcodeproj工程:检测到改动,就更新Pods.xcodeproj;如果Pods.xcodeproj不存在,默认配置生成
- 将依赖中的文件/依赖库/资源加入工程:
- 设置目标依赖(Target Dependencies)
- 不只是代码加入,还有另外的几个文件:(配置文件、编译所需的文件、资源写入文件、许可文件等)
- 包含编译选项的xcconfig/同时拥有编译设置和CPS默认配置的私有xcconfig文件;编译所需prefix.pch文件;编译必须的dummy.m文件
- 如果源代码含有资源bundle,向app中添加bundle的方式将写入Pods-Resources.sh(该脚本会将所有三方库的resource文件copy到目标目录中)
- markdown、plist为用户许可文件
- 不只是代码加入,还有另外的几个文件:(配置文件、编译所需的文件、资源写入文件、许可文件等)
- lockfile、manifest编写:写入到磁盘
Integrating(集成) client project:集成工程所需的target
- 生成workspace,管理多个工程
(CocoaPods将所有依赖库都放到一个Pods的项目中,然后主项目依赖Pods项目,这样,源码管理工作从主项目移到Pods项目中。Pods项目,最终会编译成一个名为libPods.a的文件,主项目只需要依赖这个.a文件即可)
- pod update –verbose
1
2
3
4
5
6
7
8
9
10Update all pods
Updating local specs repositories
Analyzing dependencies
- Inspecting targets to integrate
- !!!=>Finding Podfile changes
- Resolving dependencies of `Podfile`
- Comparing resolved specification to the sandbox manifest
Downloading dependencies
Generating Pods project
Integrating client project
- Updating local specs repositories
- 细心的会发现pod install没有这个过程,pod update有!这一点在下文的讲pod install和update的过程中,会提到。主要跟底层实现的默认值有关
看点CocoaPods源码
简单的抽一些源码看一下
- pod install的过程
- 在CPS中,所有命令都会由Command类派发到对应的类,而真正执行pod install的类是Install
- Install类-Installer实例-执行install!方法
- Installer的实例方法install!就会使用这些信息安装当前工程的依赖:分4部分
1
2
3
4
5
6def install!
resolve_dependencies // 解析Podfile中的依赖:会创建一个Analyzer类的实例
download_dependencies // 下载依赖
generate_pods_project // 创建 Pods.xcodeproj工程
integrate_user_project // 集成workspace
end
- resolve_dependencies
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16def resolve_dependencies
analyzer = create_analyzer
plugin_sources = run_source_provider_hooks
analyzer.sources.insert(0, *plugin_sources)
UI.section 'Updating local specs repositories' do
analyzer.update_repositories
end if repo_update?
UI.section 'Analyzing dependencies' do
analyze(analyzer)
validate_build_configurations
clean_sandbox
end
end
- 内部有一个Analyzer类
- 看到if repo_update?,是否需要更新本地local spec文件有关
- 同时,会对podfile中的依赖进行分析。内部采用Milinillo依赖关系的解决算法,算法核心是回溯及向前检查。最后会返回一个依赖图,CocoaPods拿到后,会在specs_by_target中按照target(一般工程都会有多个target)将所有的specification分组
- specification包含当前工程依赖的三方库:名字、版本、源,用于依赖的下载
- download_dependencies
我们经常会看到如下状态1
2
3-> Using AFNetworking (1.8.8)
-> Using AFNetworking (1.8.8) was (1.8.6)
-> Installing AFNetworking (1.8.8)
- 不同的状态,调用不同的方法
- create_pod_installer:只创建一个PodSourceInstaller实例,然后加入到pod_installers数组中,依赖的版本没变,无需重新下载
- nstall_source_of_pod:调用栈非常庞大,在调用栈末端Downloader.download_Source会执行CocoaPods组件CocoaPods-Download,方法中调用的for_target会根据不同的源创建一个下载器,因为依赖可能会通过不同的协议/方式进行下载,如git/http/svn等,CocoaPods-Downloader会根据依赖的参数选项使用不同的方式下载
- 大部分依赖会被下载到~/Library/Caches/CocoaPods/Pods/Release/ 文件夹中,然后从这里复制到项目工程目录下的./Pods中,完成了整个CPS的下载流程
- generate_pods_project
1
2
3
4
5
6
7
8
9
10def generate_pods_project(generator = create_generator)
UI.section 'Generating Pods project' do
generator.generate!
@pods_project = generator.project
run_podfile_post_install_hooks
generator.write
generator.share_development_pod_schemes
write_lockfiles ==>lockfile是在生成Pods.xcodeproj这一步产生的
end
end
上面调用到了PodsProjectGenerator实例方法generate!1
2
3
4
5
6def generate!
prepare
install_file_references
install_libraries
set_target_dependencies
end
- 主要做的事情:
- 生成Pods.xcodeproj工程
- 将依赖的文件、library加入工程
- 设置目标依赖,target dependencies
- 这些操作主要依赖CocoaPods/Xcodeproj组件,该组件负责所有工程文件的整合
- integrate_user_project:生成workspace,管理多个工程
UserProjectIntegrator类,调用integrate!,集成工程所需的target
1
2
3
4
5
6def integrate!
create_workspace
integrate_user_targets
warn_about_xcconfig_overrides
save_projects
end生成workspace,获取需要集成的taget,调用他们的integrate(下面附了过程代码)(跟之前描述的一样,各种配置、资源拷贝、检查manifest),将target加入到工程,使用Xcodeproj修改copy Resource Script Phrase等设置,保存project.pbxproj,整个pod install过程结束
1
2
3
4
5
6
7
8
9
10
11def integrate!
UI.section(integration_message) do
XCConfigIntegrator.integrate(target, native_targets)
add_pods_library
add_embed_frameworks_script_phase
remove_embed_frameworks_script_phase_from_embedded_targets
add_copy_resources_script_phase
add_check_manifest_lock_script_phase
end
end
Podfile.lock 和 Manifest.lock
- 生成Pods.xcodeproj中有write_lockfiles这一步
- 生成Pods.xcodeproj之前的大多工作是在内存中进行的,为了能让这些成果被重复利用,需要将结果所有结果保存到一个文件,Pods.xcodeproj文件被写入磁盘,另外生成两个重要的文件Podfile.lock,Mainfest.lock都被写入磁盘
- Podfile.lock:记录每个需要被安装的pod的每个已安装的版本(查看安装的版本)。需要加入git版本控制
- Manifest.lock:每次执行pod install时创建的Podfile.lock文件的副本。如果遇到The sandbox is not in sync with the Podfile.lock,沙盒文件与Podfile.lock文件不同步,就是因为这俩文件不一致引起。由于Pods所在的目录并不总在版本控制下,这样可以保证开发者运行app之前,都能更新他们的pods,否则app可能会crash,或者一些不太明显的地方编译失败
pod install 和 update的区别
Command类会派发给Install or Update类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27module Pod
class Command
class Install < Command
def run
verify_podfile_exists!
installer = installer_for_config
installer.repo_update = repo_update?(:default => false)
installer.update = false
installer.install!
end
end
end
end
module Pod
class Command
class Update < Command
def run
...
installer = installer_for_config
installer.repo_update = repo_update?(:default => true)
installer.update = true
installer.install!
end
end
end
end非常关键的,看内部的installer的实例对象的配置,一个repo_update,install默认值是false不更新podspec索引,update默认值是true。,这也是为什么上文在执行pod install –verbose的时候,没有
Updating local specs repositories
过程的原因- 同时还有一个update的属性,这一点我觉得应该是影响了后续的一些行为不一样的关键
- 最大区别:
- install默认不更新本地podspec索引,update默认会更新
- install并不会修改Podfile.lock中显示指定的版本
- 一般在添加/移除库,首次使用。对于.lock已存在的库,会下载明确的版本
- 不会检查是否有新的版本。不在的,会找podfile描述的版本
- 只会解决pods里但不在.lock里的库之间的依赖
- update会无视已有的Podfile.lock文件,只要符合podfile的限制,重新对依赖进行分析,尝试将所有pod更新到最新版
常见的目录&指令
目录
- ~/.cocoapods/repos
- ~/Library/Caches/CocoaPods/Pods
- 三方库在Pods目录下,同时还有Pods.xcodeproj,Manifest.lock
指令
- pod env:查看CPS版本
- pod outdated:查看Podfile文件中的依赖库的最新版本
- pod search xx:查找所有可用的库
- pod update PODNAME:忽略.lock,只更新这个库的新版本。只要符合podfile的限制
参考: