@qidiandasheng
2022-01-02T21:02:31.000000Z
字数 3645
阅读 897
Cocoapods
CLAide 是一个简单的命令行解释器,它提供了功能齐全的命令行界面和 API。它不仅负责解析我们使用到的 Pods 命令,例如:pod install
, pod update
等,还可用于封装常用的一些脚本,将其打包成简单的命令行小工具。
我们先通过 pod --help
来查看 CLAide 的真实输出效果:
$ pod
Usage:
$ pod COMMAND
CocoaPods, the Cocoa library package manager.
Commands:
+ cache Manipulate the CocoaPods cache
+ deintegrate Deintegrate CocoaPods from your project
+ env Display pod environment
+ init Generate a Podfile for the current directory
...
Options:
--allow-root Allows CocoaPods to run as root
--silent Show nothing
--version Show the version of the tool
...
👆所展示的Usage
、Commands
、Options
及其内容均是由 CALid
e 的输出模版Banner
来完成的。CALide
提供了 Command
基类帮助我们快速定义出标准且美观的命令。除了 pod 命令之外,例如:Xcodeproj
所提供的命令也是由 CALide
来实现的。
首先来看 CALide 项目的文件入口 lib/calide.rb
:
module CLAide
VERSION = '1.0.3'.freeze
require 'claide/ansi'
require 'claide/argument'
require 'claide/argv'
require 'claide/command'
require 'claide/help'
require 'claide/informative_error'
end
Command
是用于构建命令行界面的基础抽象类。所有我们添加的命令都需要继承自Command
,这些子类可以嵌套组合成更加精细的命令。
pod 命令正是由多个 Pod::Command < CLAide::Command
的子类组合而成的 Abstract command
。当然 pod 的 subcommand
同样也能声明为 abstact command
,通过这样的方式我们就能达到多级嵌套命令的效果。有抽象命令当然也需要有具体执行任务的 normal command
。
对于任何命令类型都可以设置以下几个属性和方法:
Abstract command
为不提供具体命令实现的抽象容器命令类,不过它可以包含一个或多个的subcommands
。
比如我们最简单的pod命令为Abstract Command
,就是继承自CLAide::Command
:
module Pod
class Command < CLAide::Command
require 'cocoapods/command/options/repo_update'
require 'cocoapods/command/options/project_directory'
include Options
require 'cocoapods/command/cache'
require 'cocoapods/command/env'
require 'cocoapods/command/init'
require 'cocoapods/command/install'
require 'cocoapods/command/ipc'
require 'cocoapods/command/lib'
require 'cocoapods/command/list'
require 'cocoapods/command/outdated'
require 'cocoapods/command/repo'
require 'cocoapods/command/setup'
require 'cocoapods/command/spec'
require 'cocoapods/command/update'
# 表示为Abstract Command
self.abstract_command = true
self.command = 'pod'
self.version = VERSION
self.description = 'CocoaPods, the Cocoa library package manager.'
self.plugin_prefixes = %w(claide cocoapods)
.
.
.
.
end
end
相对于抽象命令,普通命令就需要设置传递实参的名称和描述,以及重载 run 方法。
比如继承自Command
的pod子命令pod update
:
module Pod
class Command
class Update < Command
self.arguments = [
CLAide::Argument.new('POD_NAMES', false, true),
]
self.description = <<-DESC
Updates the Pods identified by the specified `POD_NAMES`.
DESC
def self.options
[
["--sources", 'The sources from which to update dependent pods'],
['--exclude-pods', 'Pods to exclude during update'],
['--clean-install', 'Ignore the contents of the project cache and force a full pod installation']
].concat(super)
end
end
end
end
在 Command
类中定义了两个run
方法:
def self.run(argv = [])
# 根据文件前缀来匹配对应的插件
plugin_prefixes.each do |plugin_prefix|
PluginManager.load_plugins(plugin_prefix)
end
argv = ARGV.coerce(argv)
# 解析 argument 生成对应的 command instance
command = parse(argv)
ANSI.disabled = !command.ansi_output?
unless command.handle_root_options(argv)
command.validate!
command.run
end
rescue Object => exception
handle_exception(command, exception)
end
def run
raise 'A subclass should override the `CLAide::Command#run` method to ' \
'actually perform some work.'
end
作为Command
类的核心方法,类方法 self.run
将终端传入的参数解析成对应的 command
和 argv
,并最终调用 command
的实例方法 run
来触发真正的命令逻辑。因此子类需要通过重载 run
方法来完成对应命令的实现。
比如pod install
的run方法:
module Pod
class Command
class Install < Command
# ...
def run
# 判断是否存在 Podfile 文件
verify_podfile_exists!
# 从 Config 中获取一个 Instraller 实例
installer = installer_for_config
# 默认是不执行 update
installer.repo_update = repo_update?(:default => false)
installer.update = false
installer.deployment = @deployment
# 忽略工程中的缓存,直接全量编译,默认为false
installer.clean_install = @clean_install
# install 的真正过程
installer.install!
end
end
end
end