@qidiandasheng
2022-01-02T13:02:31.000000Z
字数 3645
阅读 1468
Cocoapods
CLAide 是一个简单的命令行解释器,它提供了功能齐全的命令行界面和 API。它不仅负责解析我们使用到的 Pods 命令,例如:pod install, pod update等,还可用于封装常用的一些脚本,将其打包成简单的命令行小工具。
我们先通过 pod --help 来查看 CLAide 的真实输出效果:
$ podUsage:$ pod COMMANDCocoaPods, 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 及其内容均是由 CALide 的输出模版Banner 来完成的。CALide 提供了 Command 基类帮助我们快速定义出标准且美观的命令。除了 pod 命令之外,例如:Xcodeproj 所提供的命令也是由 CALide 来实现的。
首先来看 CALide 项目的文件入口 lib/calide.rb:
module CLAideVERSION = '1.0.3'.freezerequire '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 Podclass Command < CLAide::Commandrequire 'cocoapods/command/options/repo_update'require 'cocoapods/command/options/project_directory'include Optionsrequire '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 Commandself.abstract_command = trueself.command = 'pod'self.version = VERSIONself.description = 'CocoaPods, the Cocoa library package manager.'self.plugin_prefixes = %w(claide cocoapods)....endend
相对于抽象命令,普通命令就需要设置传递实参的名称和描述,以及重载 run 方法。
比如继承自Command的pod子命令pod update:
module Podclass Commandclass Update < Commandself.arguments = [CLAide::Argument.new('POD_NAMES', false, true),]self.description = <<-DESCUpdates the Pods identified by the specified `POD_NAMES`.DESCdef 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)endendendend

在 Command 类中定义了两个run方法:
def self.run(argv = [])# 根据文件前缀来匹配对应的插件plugin_prefixes.each do |plugin_prefix|PluginManager.load_plugins(plugin_prefix)endargv = ARGV.coerce(argv)# 解析 argument 生成对应的 command instancecommand = parse(argv)ANSI.disabled = !command.ansi_output?unless command.handle_root_options(argv)command.validate!command.runendrescue Object => exceptionhandle_exception(command, exception)enddef runraise '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 Podclass Commandclass Install < Command# ...def run# 判断是否存在 Podfile 文件verify_podfile_exists!# 从 Config 中获取一个 Instraller 实例installer = installer_for_config# 默认是不执行 updateinstaller.repo_update = repo_update?(:default => false)installer.update = falseinstaller.deployment = @deployment# 忽略工程中的缓存,直接全量编译,默认为falseinstaller.clean_install = @clean_install# install 的真正过程installer.install!endendendend