cocoapods源码1 - bin

匿了很长一段时间了,但是仍然一直处于学习ing...刚好最近项目也需要用到cocoapods的一些技巧,且有次面试也问我是否看过cocoapods源码...于是决定看下...

1、cocoapods 源码

1. cocoapods 包含多个模块

Snip20180812_7的副本.png

2. cocoapods-1.5.3(cocoapods) 目录

➜  CocoaPods git:(master) ls -l
total 624
-rw-r--r--   1 xiongzenghui  staff  257140  7 24 10:31 CHANGELOG.md
-rw-r--r--   1 xiongzenghui  staff    3387  7 24 10:31 CODE_OF_CONDUCT.md
-rw-r--r--   1 xiongzenghui  staff    5966  7 24 10:31 CONTRIBUTING.md
-rw-r--r--   1 xiongzenghui  staff    1367  7 24 10:31 Dangerfile
-rw-r--r--   1 xiongzenghui  staff    1892  7 24 10:31 Gemfile
-rw-r--r--   1 xiongzenghui  staff    6770  7 24 10:31 Gemfile.lock
-rw-r--r--   1 xiongzenghui  staff    1591  7 24 10:31 LICENSE
-rw-r--r--   1 xiongzenghui  staff    3585  7 24 10:31 NOMENCLATURE.md
-rw-r--r--   1 xiongzenghui  staff    6043  7 24 10:31 README.md
-rw-r--r--   1 xiongzenghui  staff   11531  7 24 10:31 Rakefile
drwxr-xr-x   4 xiongzenghui  staff     128  7 24 10:31 bin
-rw-r--r--   1 xiongzenghui  staff    3172  7 24 10:31 cocoapods.gemspec
drwxr-xr-x  10 xiongzenghui  staff     320  7 24 10:31 examples
drwxr-xr-x   4 xiongzenghui  staff     128  7 24 10:31 lib
drwxr-xr-x  10 xiongzenghui  staff     320  7 24 10:31 spec
➜  CocoaPods git:(master)

2、cocoapods.gemspec

# encoding: UTF-8
require File.expand_path('../lib/cocoapods/gem_version', __FILE__)
require 'date'

Gem::Specification.new do |s|
  s.name     = "cocoapods"
  s.version  = Pod::VERSION
  s.date     = Date.today
  s.license  = "MIT"
  s.email    = ["eloy.de.enige@gmail.com", "fabiopelosin@gmail.com", "kyle@fuller.li", "segiddins@segiddins.me"]
  s.homepage = "https://github.com/CocoaPods/CocoaPods"
  s.authors  = ["Eloy Duran", "Fabio Pelosin", "Kyle Fuller", "Samuel Giddins"]

  s.summary     = "The Cocoa library package manager."
  s.description = "CocoaPods manages library dependencies for your Xcode project.\n\n"     \
                  "You specify the dependencies for your project in one easy text file. "  \
                  "CocoaPods resolves dependencies between libraries, fetches source "     \
                  "code for the dependencies, and creates and maintains an Xcode "         \
                  "workspace to build your project.\n\n"                                   \
                  "Ultimately, the goal is to improve discoverability of, and engagement " \
                  "in, third party open-source libraries, by creating a more centralized " \
                  "ecosystem."

  s.files = Dir["lib/**/*.rb"] + %w{ bin/pod bin/sandbox-pod README.md LICENSE CHANGELOG.md }

  s.executables   = %w{ pod sandbox-pod }
  s.require_paths = %w{ lib }

  # Link with the version of CocoaPods-Core
  s.add_runtime_dependency 'cocoapods-core',        "= #{Pod::VERSION}"
  s.add_runtime_dependency 'claide',                '>= 1.0.2', '< 2.0'
  s.add_runtime_dependency 'cocoapods-deintegrate', '>= 1.0.2', '< 2.0'
  s.add_runtime_dependency 'cocoapods-downloader',  '>= 1.2.1', '< 2.0'
  s.add_runtime_dependency 'cocoapods-plugins',     '>= 1.0.0', '< 2.0'
  s.add_runtime_dependency 'cocoapods-search',      '>= 1.0.0', '< 2.0'
  s.add_runtime_dependency 'cocoapods-stats',       '>= 1.0.0', '< 2.0'
  s.add_runtime_dependency 'cocoapods-trunk',       '>= 1.3.0', '< 2.0'
  s.add_runtime_dependency 'cocoapods-try',         '>= 1.1.0', '< 2.0'
  s.add_runtime_dependency 'molinillo',             '~> 0.6.5'
  s.add_runtime_dependency 'xcodeproj',             '>= 1.5.8', '< 2.0'

  ## Version 5 needs Ruby 2.2, so we specify an upper bound to stay compatible with system ruby
  s.add_runtime_dependency 'activesupport', '>= 4.0.2', '< 5'
  s.add_runtime_dependency 'colored2',       '~> 3.1'
  s.add_runtime_dependency 'escape',        '~> 0.0.4'
  s.add_runtime_dependency 'fourflusher',   '~> 2.0.1'
  s.add_runtime_dependency 'gh_inspector',  '~> 1.0'
  s.add_runtime_dependency 'nap',           '~> 1.0'
  s.add_runtime_dependency 'ruby-macho',    '~> 1.2'

  s.add_development_dependency 'bacon', '~> 1.1'
  s.add_development_dependency 'bundler', '~> 1.3'
  s.add_development_dependency 'rake', '~> 10.0'

  ## Make sure you can build the gem on older versions of RubyGems too:
  s.rubygems_version = "1.6.2"
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
  s.required_ruby_version = '>= 2.0.0'
  s.specification_version = 3 if s.respond_to? :specification_version
end
  • 1、lib/**/*.rb
  • 2、pod、sandbox-pod
  • 3、cocoapods 组成结构
s.add_runtime_dependency 'cocoapods-core',        "= #{Pod::VERSION}"
s.add_runtime_dependency 'claide',                '>= 1.0.2', '< 2.0'
s.add_runtime_dependency 'cocoapods-deintegrate', '>= 1.0.2', '< 2.0'
s.add_runtime_dependency 'cocoapods-downloader',  '>= 1.2.1', '< 2.0'
s.add_runtime_dependency 'cocoapods-plugins',     '>= 1.0.0', '< 2.0'
s.add_runtime_dependency 'cocoapods-search',      '>= 1.0.0', '< 2.0'
s.add_runtime_dependency 'cocoapods-stats',       '>= 1.0.0', '< 2.0'
s.add_runtime_dependency 'cocoapods-trunk',       '>= 1.3.0', '< 2.0'
s.add_runtime_dependency 'cocoapods-try',         '>= 1.1.0', '< 2.0'
s.add_runtime_dependency 'molinillo',             '~> 0.6.5'
s.add_runtime_dependency 'xcodeproj',             '>= 1.5.8', '< 2.0'

3、Gemfile

SKIP_UNRELEASED_VERSIONS = false

# Declares a dependency to the git repo of CocoaPods gem. This declaration is
# compatible with the local git repos feature of Bundler.
#
def cp_gem(name, repo_name, branch = 'master', path: false)
  # 1、调用gem()直接从rubygems服务器下载库代码
  return gem name if SKIP_UNRELEASED_VERSIONS
  # return gem(name) if SKIP_UNRELEASED_VERSIONS

  # 2.1 构造参数Hash
  opts = if path
          # 指定从【本地目录】读取依赖的库代码
          { :path => "../#{repo_name}" }
        else
          # 指定从【cocoapods服务器】下载依赖的库代码
          url = "https://github.com/CocoaPods/#{repo_name}.git"
          { :git => url, :branch => branch }
        end

  # 3、gem()
  gem name, opts
  # gem(name, opts)
end

source 'https://rubygems.org'

gemspec

# This is the version that ships with OS X 10.10, so be sure we test against it.
# At the same time, the 1.7.7 version won't install cleanly on Ruby > 2.2,
# so we use a fork that makes a trivial change to a macro invocation.
gem 'json', :git => 'https://github.com/segiddins/json.git', :branch => 'seg-1.7.7-ruby-2.2'

group :development do
  cp_gem 'claide',                'CLAide'
  cp_gem 'cocoapods-core',        'Core'
  cp_gem 'cocoapods-deintegrate', 'cocoapods-deintegrate'
  cp_gem 'cocoapods-downloader',  'cocoapods-downloader'
  cp_gem 'cocoapods-plugins',     'cocoapods-plugins'
  cp_gem 'cocoapods-search',      'cocoapods-search'
  cp_gem 'cocoapods-stats',       'cocoapods-stats'
  cp_gem 'cocoapods-trunk',       'cocoapods-trunk'
  cp_gem 'cocoapods-try',         'cocoapods-try'
  cp_gem 'molinillo',             'Molinillo'
  cp_gem 'nanaimo',               'Nanaimo'
  cp_gem 'xcodeproj',             'Xcodeproj'

  gem 'cocoapods-dependencies', '~> 1.0.beta.1'

  gem 'bacon'
  gem 'mocha'
  gem 'mocha-on-bacon'
  gem 'prettybacon'
  gem 'webmock'

  # Integration tests
  gem 'diffy'
  gem 'clintegracon'

  # Code Quality
  gem 'inch_by_inch'
  gem 'rubocop'

  gem 'danger'
end

group :debugging do
  gem 'cocoapods_debug'

  gem 'rb-fsevent'
  gem 'kicker'
  gem 'awesome_print'
  gem 'ruby-prof', :platforms => [:ruby]
end
  • 1、group :development
  • 2、group :debugging

4、bin目录包含2个可执行文件

➜  bin git:(master) ls -l
total 24
-rwxr-xr-x@ 1 xiongzenghui  staff  1443  7 24 10:31 pod
-rwxr-xr-x@ 1 xiongzenghui  staff  4266  7 24 10:31 sandbox-pod
➜  bin git:(master)

5、pod

0. 本地rubygems包存储目录

/Users/xiongzenghui/.rvm/gems/ruby-2.3.0@global/gems/cocoapods-1.5.3/bin

1. rb源文件

  • 1、就是一个ruby源文件
  • 2、通过#!/usr/bin/env ruby指定搜索PATH环境变量中的ruby解释器进行解释执行rb文件
#!/usr/bin/env ruby

# 1、如果编码不是UTF-8,则输出错误信息到stderr
if Encoding.default_external != Encoding::UTF_8
  if ARGV.include? '--no-ansi'
    STDERR.puts <<-DOC
    WARNING: CocoaPods requires your terminal to be using UTF-8 encoding.
    Consider adding the following to ~/.profile:

    export LANG=en_US.UTF-8
    DOC
  else
    STDERR.puts <<-DOC
    \e[33mWARNING: CocoaPods requires your terminal to be using UTF-8 encoding.
    Consider adding the following to ~/.profile:

    export LANG=en_US.UTF-8
    \e[0m
    DOC
  end
end

# 2、确定从哪里导入cocoapods库
# - 1)本地:../../lib/
# - 2)rubygems服务器:https://rubygems.org
if $PROGRAM_NAME == __FILE__ && !ENV['COCOAPODS_NO_BUNDLER']
  # 找到 Gemfile 文件,添加到ENV环境变量map中
  ENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', __FILE__)
  # 导入系统rb库
  require 'rubygems'
  require 'bundler/setup'
  # 将cocoapods源码根目录下的lib目录,添加到$LOAD_PATH记录的加载rb文件的搜索目录集合中
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
elsif ENV['COCOAPODS_NO_BUNDLER'] 
  require 'rubygems'
  gem 'cocoapods' # 从 https://rubygems.org 共有仓库,下载cocoapods库
end

# 3、
STDOUT.sync = true if ENV['CP_STDOUT_SYNC'] == 'TRUE'

# 4、导入 cocoapods.rb
require 'cocoapods'

# 5、执行pod命令时,是否带有 PROFILE=xxx 选项
# - 1)有:
#   - 调用 ruby-prof 分析rb代码的执行效率
#   - 再调用 Pod::Command.run(ARGV) 解析命令行参数
#   - https://ruby-china.org/topics/25959
# - 2)没有:
#   - 直接 Pod::Command.run(ARGV) 解析命令行参数
#
# - 3)Pod::Command.run(ARGV) 负责调用claide构建命令行选项
if profile_filename = ENV['PROFILE']
  require 'ruby-prof'
  reporter =
    case (profile_extname = File.extname(profile_filename))
    when '.txt'
      RubyProf::FlatPrinterWithLineNumbers
    when '.html'
      RubyProf::GraphHtmlPrinter
    when '.callgrind'
      RubyProf::CallTreePrinter
    else
      raise "Unknown profiler format indicated by extension: #{profile_extname}"
    end
  File.open(profile_filename, 'w') do |io|
    reporter.new(RubyProf.profile { 
      Pod::Command.run(ARGV) # Pod模块中的Command类的self.run()类方法
    }).print(io)
  end
else
  Pod::Command.run(ARGV) # Pod模块中的Command类的self.run()类方法
end

2. 要点梳理

1. PROFILE 命令行参数

PROFILE=tmp.txt pod install

2. ENV['COCOAPODS_NO_BUNDLER'] == 1 执行如下逻辑

elsif ENV['COCOAPODS_NO_BUNDLER'] 
  require 'rubygems'
  gem 'cocoapods' # 从 https://rubygems.org 共有仓库,下载安装cocoapods库
end

3. 调用claide构建命令行选项

Pod::Command.run(ARGV)

构造pod命令行选项如下

➜  ~ 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
    + install       Install project dependencies according to versions from a
                    Podfile.lock
    + ipc           Inter-process communication
    + lib           Develop pods
    + list          List pods
    + outdated      Show outdated project dependencies
    + plugins       Show available CocoaPods plugins
    + repo          Manage spec-repositories
    + search        Search for pods
    + setup         Setup the CocoaPods environment
    + spec          Manage pod specs
    + trunk         Interact with the CocoaPods API (e.g. publishing new specs)
    + try           Try a Pod!
    + update        Update outdated project dependencies and create new Podfile.lock

Options:

    --silent        Show nothing
    --version       Show the version of the tool
    --verbose       Show more debugging information
    --no-ansi       Show output without ANSI codes
    --help          Show help banner of specified command
➜  ~

6、sandbox-pod

0. 本地rubygems包存储目录

/Users/xiongzenghui/.rvm/gems/ruby-2.3.0@global/gems/cocoapods-1.5.3/bin

1. rb源文件

#!/usr/bin/env ruby
# encoding: utf-8

# This bin wrapper runs the `pod` command in a OS X sandbox. The reason for this
# is to ensure that people can’t use malicious code from pod specifications.
#
# It does this by creating a ‘seatbelt’ profile on the fly and executing the
# given command through `/usr/bin/sandbox-exec`. This profile format is an
# undocumented format, which uses TinyScheme to implement its DSL.
#
# Even though it uses a undocumented format, it’s actually very self-explanatory.
# Because we use a whitelist approach, `(deny default)`, any action that is
# denied is logged to `/var/log/system.log`. So tailing that should provide
# enough information on steps that need to be take to get something to work.
#
# For more information see:
#
# * https://github.com/CocoaPods/CocoaPods/issues/939
# * http://reverse.put.as/wp-content/uploads/2011/08/The-Apple-Sandbox-BHDC2011-Slides.pdf
# * http://reverse.put.as/wp-content/uploads/2011/08/The-Apple-Sandbox-BHDC2011-Paper.pdf
# * https://github.com/s7ephen/OSX-Sandbox--Seatbelt--Profiles
# * `$ man sandbox-exec`
# * `$ ls /usr/share/sandbox`

# 1、单独执行 sandbox-pod(rb文件)
if $0 == __FILE__
  $:.unshift File.expand_path('../../lib', __FILE__) # 追加本地的lib目录为require搜索路径
end

# 2、require xxx.rb
require 'pathname'
require 'cocoapods/config' # lib/cocoapods/config.rb
require 'rbconfig'
require 'erb'

PROFILE_ERB_TEMPLATE = <<-EOS
(version 1)
(debug allow)

(import "mDNSResponder.sb")

(allow file-ioctl)
(allow sysctl-read)
(allow mach-lookup)
(allow ipc-posix-shm)
(allow process-fork)
(allow system-socket)

; TODO make this stricter if possible
(allow network-outbound)

(allow process-exec
  (literal
    "<%= pod_bin %>"
    "<%= ruby_bin %>"
  )
  (regex
<% prefixes.each do |prefix| %>
    #"^<%= prefix %>/*"
<% end %>
  )
)

(allow file-read-metadata)
(allow file-read*
  ; This is currenly only added because using `xcodebuild` to build a resource
  ; bundle target starts a FSEvents stream on `/`. No idea why that would be
  ; needed, but for now it doesn’t seem like a real problem.
  (literal "/")
  (regex
    ; TODO see if we can restrict this more, but it's going to be hard
    #"^/Users/[^.]+/*"
    ;#"^/Users/[^.]+/.netrc"
    ;#"^/Users/[^.]+/.gemrc"
    ;#"^/Users/[^.]+/.gem/*"
    ;#"^/Users/[^.]+/Library/.*"
    #"^/Library/*"
    #"^/System/Library/*"
    #"^/usr/lib/*"
    #"^/usr/share/*"
    #"^/private/*"
    #"^/dev/*"
    #"^<%= ruby_prefix %>"
    #"^<%= pod_prefix %>"
    #"^<%= xcode_app_path %>"
    #"^<%= Pod::Config.instance.repos_dir %>"
<% prefixes.each do |prefix| %>
    #"^<%= prefix %>/*"
<% end %>
  )
)

(allow file-write*
  (literal
    "/dev/dtracehelper"
    "/dev/null"
  )
  (regex
    #"^<%= Pod::Config.instance.project_root %>"
    #"^<%= Pod::Config.instance.repos_dir %>"
    #"^/Users/[^.]+/Library/Caches/CocoaPods/*"
    #"^/dev/tty"
    #"^/private/var"
  )
)

(deny default)
EOS

class Profile
  def pod_bin
    File.expand_path('../pod', __FILE__)
  end

  def pod_prefix
    File.expand_path('../..', pod_bin)
  end

  def ruby_bin
    File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
  end

  def ruby_prefix
    RbConfig::CONFIG['prefix']
  end

  def prefix_from_bin(bin_name)
    unless (path = `which #{bin_name}`.strip).empty?
      File.dirname(File.dirname(path))
    end
  end

  def prefixes
    prefixes = ['/bin', '/usr/bin', '/usr/libexec', xcode_app_path]
    prefixes << `brew --prefix`.strip unless `which brew`.strip.empty?

    # From asking people, it seems MacPorts does not have a `prefix` command, like
    # Homebrew does, so make an educated guess:
    if port_prefix = prefix_from_bin('port')
      prefixes << port_prefix
    end

    if rbenv_prefix = prefix_from_bin('rbenv')
      prefixes << rbenv_prefix
    end

    prefixes
  end

  def developer_prefix
    `xcode-select --print-path`.strip
  end

  def xcode_app_path
    File.expand_path('../..', developer_prefix)
  end

  # TODO: raise SAFE level (0) to 4 if possible.
  def generate
    ERB.new(PROFILE_ERB_TEMPLATE, 0, '>').result(binding)
  end
end

# Ensure the `pod` bin doesn’t think it needs to use Bundler.
ENV['COCOAPODS_NO_BUNDLER'] = '1'

profile = Profile.new
# puts profile.generate

# exec() 执行数组保证的shell命令+选项+选项值
command = ['/usr/bin/sandbox-exec', '-p', profile.generate, profile.pod_bin, *ARGV]
exec(*command)

2. 要点梳理

  • 1、This bin wrapper runs the pod command in a OS X sandbox
  • 2、any action that is denied is logged to /var/log/system.log
  • 3、ENV['COCOAPODS_NO_BUNDLER'] = '1' ,则会影响 pod
  • 4、最终调用 /usr/bin/sandbox-exec
  • 5、exec() 执行数组保证的shell命令+选项+选项值
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343