class Motion::Project::CocoaPods

Constants

PODS_ROOT
PUBLIC_HEADERS_ROOT
SUPPORT_FILES
TARGET_NAME
VERSION

Attributes

podfile[RW]

Public Class Methods

new(config, vendor_options) click to toggle source
# File lib/motion/project/cocoapods.rb, line 80
def initialize(config, vendor_options)
  @config = config
  @vendor_options = vendor_options
  @use_frameworks = false

  case @config.deploy_platform
  when 'MacOSX'
    platform = :osx
  when 'iPhoneOS'
    platform = :ios
  when 'AppleTVOS'
    platform = :tvos
  when 'WatchOS'
    platform = :watchos
  else
    App.fail "Unknown CocoaPods platform: #{@config.deploy_platform}"
  end

  @podfile = Pod::Podfile.new(Pathname.new(Rake.original_dir) + 'Rakefile') do
    platform(platform, config.deployment_target)
    target(TARGET_NAME)
    install!('cocoapods', :integrate_targets => false)
  end
  cp_config.podfile = @podfile
  cp_config.installation_root = Pathname.new(File.expand_path(config.project_dir)) + 'vendor'

  if cp_config.verbose = !!ENV['COCOAPODS_VERBOSE']
    require 'claide'
  end
end

Public Instance Methods

analyzer() click to toggle source
# File lib/motion/project/cocoapods.rb, line 320
def analyzer
  Pod::Installer::Analyzer.new(cp_config.sandbox, @podfile, cp_config.lockfile)
end
configure_project() click to toggle source

Adds the Pods project to the RubyMotion config as a vendored project and

# File lib/motion/project/cocoapods.rb, line 113
def configure_project
  if (xcconfig = self.pods_xcconfig_hash) && ldflags = xcconfig['OTHER_LDFLAGS']
    @config.resources_dirs << resources_dir.to_s

    frameworks = installed_frameworks[:pre_built]
    if frameworks
      @config.embedded_frameworks += frameworks
      @config.embedded_frameworks.uniq!
    end

    if @use_frameworks
      configure_project_frameworks
    else
      configure_project_static_libraries
    end
  end
end
configure_project_frameworks() click to toggle source
# File lib/motion/project/cocoapods.rb, line 131
def configure_project_frameworks
  if (xcconfig = self.pods_xcconfig_hash) && ldflags = xcconfig['OTHER_LDFLAGS']
    # Add libraries to @config.libs
    pods_libraries

    frameworks = ldflags.scan(/-framework\s+"?([^\s"]+)"?/).map { |m| m[0] }
    if build_frameworks = installed_frameworks[:build]
      build_frameworks = build_frameworks.map { |path| File.basename(path, ".framework") }
      frameworks.delete_if { |f| build_frameworks.include?(f) }
    end
    static_frameworks = pods_frameworks(frameworks)
    static_frameworks_paths = static_frameworks_paths(static_frameworks)
    search_path = static_frameworks_paths.inject("") { |s, path|
      s += " -I'#{path}' -I'#{path}/Headers'"
    }
    @vendor_options[:bridgesupport_cflags] ||= ''
    @vendor_options[:bridgesupport_cflags] << " #{header_search_path} #{framework_search_path} #{search_path}"

    @config.weak_frameworks.concat(ldflags.scan(/-weak_framework\s+([^\s]+)/).map { |m| m[0] })
    @config.weak_frameworks.uniq!

    vendors = @config.vendor_project(PODS_ROOT, :xcode, {
      :target => "Pods-#{TARGET_NAME}",
      :products => build_frameworks.map { |name| "#{name}.framework" },
      :allow_empty_products => build_frameworks.empty?,
    }.merge(@vendor_options))

    vendor = vendors.last
    if vendor.respond_to?(:build_bridgesupport)
      static_frameworks_paths.each do |path|
        path = File.expand_path(path)
        bs_file = File.join(Builder.common_build_dir, "#{path}.bridgesupport")
        headers = Dir.glob(File.join(path, '**{,/*/**}/*.h'))
        @config.vendor_project(PODS_ROOT, :bridgesupport, {
          :bs_file => bs_file,
          :headers => headers,
        }.merge(@vendor_options))
      end
    end
  end
end
configure_project_static_libraries() click to toggle source
# File lib/motion/project/cocoapods.rb, line 173
def configure_project_static_libraries
  # TODO replace this all once Xcodeproj has the proper xcconfig parser.
  if (xcconfig = self.pods_xcconfig_hash) && ldflags = xcconfig['OTHER_LDFLAGS']
    # Collect the Pod products
    pods_libs = pods_libraries

    # Initialize ':bridgesupport_cflags', in case the use
    @vendor_options[:bridgesupport_cflags] ||= ''
    @vendor_options[:bridgesupport_cflags] << " #{header_search_path} #{framework_search_path}"

    frameworks = ldflags.scan(/-framework\s+"?([^\s"]+)"?/).map { |m| m[0] }
    pods_frameworks(frameworks)

    @config.weak_frameworks.concat(ldflags.scan(/-weak_framework\s+([^\s]+)/).map { |m| m[0] })
    @config.weak_frameworks.uniq!

    @config.vendor_project(PODS_ROOT, :xcode, {
      :target => "Pods-#{TARGET_NAME}",
      :headers_dir => "Headers/Public",
      :products => pods_libs.map { |lib_name| "lib#{lib_name}.a" },
      :allow_empty_products => pods_libs.empty?,
    }.merge(@vendor_options))
  end
end
copy_cocoapods_env_and_prefix_headers() click to toggle source
# File lib/motion/project/cocoapods.rb, line 291
def copy_cocoapods_env_and_prefix_headers
  headers = Dir.glob(["#{PODS_ROOT}/*.h", "#{PODS_ROOT}/*.pch", "#{PODS_ROOT}/Target Support Files/**/*.h", "#{PODS_ROOT}/Target Support Files/**/*.pch"])
  headers.each do |header|
    src = File.basename(header)
    dst = src.sub(/\.pch$/, '.h')
    dst_path = File.join(PUBLIC_HEADERS_ROOT, "____#{dst}")
    unless File.exist?(dst_path)
      FileUtils.mkdir_p(PUBLIC_HEADERS_ROOT)
      FileUtils.cp(header, dst_path)
    end
  end
end
cp_config() click to toggle source
# File lib/motion/project/cocoapods.rb, line 316
def cp_config
  Pod::Config.instance
end
dependency(*name_and_version_requirements, &block) click to toggle source

Deprecated.

# File lib/motion/project/cocoapods.rb, line 210
def dependency(*name_and_version_requirements, &block)
  @podfile.dependency(*name_and_version_requirements, &block)
end
framework_public_headers_dir(framework_path) click to toggle source
# File lib/motion/project/cocoapods.rb, line 250
def framework_public_headers_dir(framework_path)
  framework_name = framework_path[%r{/([^/]*)\.framework/}, 1]
  File.join(@config.project_dir, PUBLIC_HEADERS_ROOT, framework_name)
end
framework_search_path() click to toggle source
# File lib/motion/project/cocoapods.rb, line 471
def framework_search_path
  framework_search_paths.map { |p| "-F'#{File.expand_path(File.join(p, '..'))}'" }.join(' ')
end
framework_search_paths() click to toggle source
# File lib/motion/project/cocoapods.rb, line 445
def framework_search_paths
  @framework_search_paths ||= begin
    xcconfig = pods_xcconfig_hash

    paths = []
    if search_paths = xcconfig['FRAMEWORK_SEARCH_PATHS']
      search_paths = search_paths.strip
      unless search_paths.empty?
        search_paths.scan(/"([^"]+)"/) do |search_path|
          path = search_path.first.gsub!(/(\$\(PODS_ROOT\))|(\$\{PODS_ROOT\})/, "#{@config.project_dir}/#{PODS_ROOT}")
          paths << path if path
        end
        # If we couldn't parse any search paths, then presumably nothing was properly quoted, so
        # fallback to just assuming the whole value is one path.
        if paths.empty?
          path = search_paths.gsub!(/(\$\(PODS_ROOT\))|(\$\{PODS_ROOT\})/, "#{@config.project_dir}/#{PODS_ROOT}")
          paths << path if path
        end
      end
    end
    paths
  end

  @framework_search_paths
end
header_search_path() click to toggle source
# File lib/motion/project/cocoapods.rb, line 506
def header_search_path
  header_search_paths.map { |p| "-I'#{p}'" }.join(' ')
end
header_search_paths() click to toggle source
# File lib/motion/project/cocoapods.rb, line 475
def header_search_paths
  @header_search_paths ||= begin
    xcconfig = pods_xcconfig_hash

    paths = []
    if search_paths = xcconfig['HEADER_SEARCH_PATHS']
      search_paths = search_paths.strip
      unless search_paths.empty?
        search_paths.scan(/"([^"]+)"/) do |search_path|
          path = search_path.first.gsub!(/(\$\(PODS_ROOT\))|(\$\{PODS_ROOT\})/, "#{@config.project_dir}/#{PODS_ROOT}")
          paths << File.expand_path(path) if path
        end
        # If we couldn't parse any search paths, then presumably nothing was properly quoted, so
        # fallback to just assuming the whole value is one path.
        if paths.empty?
          path = search_paths.gsub!(/(\$\(PODS_ROOT\))|(\$\{PODS_ROOT\})/, "#{@config.project_dir}/#{PODS_ROOT}")
          paths << File.expand_path(path) if path
        end
      end
    end
    framework_search_paths.each do |framework_search_path|
      Dir.glob("#{framework_search_path}/*.framework/Headers").each do |framework_path|
        paths << framework_public_headers_dir(framework_path)
      end
    end
    paths
  end

  @header_search_paths
end
inspect() click to toggle source

This is the output that gets shown in `rake config`, so it should be short and sweet.

# File lib/motion/project/cocoapods.rb, line 310
def inspect
  cp_config.lockfile.to_hash['PODS'].map do |pod|
    pod.is_a?(Hash) ? pod.keys.first : pod
  end.inspect
end
install!(update) click to toggle source

Performs a CocoaPods Installation.

For now we only support one Pods target, this will have to be expanded once we work on more spec support.

Let RubyMotion re-generate the BridgeSupport file whenever the list of installed pods changes.

# File lib/motion/project/cocoapods.rb, line 238
def install!(update)
  FileUtils.rm_rf(resources_dir)

  pods_installer.update = update
  pods_installer.install!
  symlink_framework_headers
  install_resources
  copy_cocoapods_env_and_prefix_headers
end
install_resources() click to toggle source

TODO this probably breaks in cases like resource bundles etc, need to test.

# File lib/motion/project/cocoapods.rb, line 270
def install_resources
  FileUtils.mkdir_p(resources_dir)

  installed_resources = []
  resources.each do |file|
    begin
      dst = resources_dir + file.basename
      if file.exist? && !dst.exist?
        FileUtils.cp_r(file, resources_dir)
        installed_resources << dst
      end
    rescue ArgumentError => exc
      unless exc.message =~ /same file/
        raise
      end
    end
  end

  installed_resources
end
installed_frameworks() click to toggle source
# File lib/motion/project/cocoapods.rb, line 551
def installed_frameworks
  return @installed_frameworks if @installed_frameworks

  @installed_frameworks = {}
  path = Pathname.new(@config.project_dir) + SUPPORT_FILES + "Pods-#{TARGET_NAME}-frameworks.sh"
  return @installed_frameworks unless path.exist?

  @installed_frameworks[:pre_built] = []
  @installed_frameworks[:build] = []

  File.open(path) { |f|
    f.each_line do |line|
      if matched = line.match(/install_framework\s+(.*)/)
        path = (matched[1].strip)[1..-2]
        if path.include?('${PODS_ROOT}')
          path = path.sub('${PODS_ROOT}', PODS_ROOT)
          @installed_frameworks[:pre_built] << File.join(@config.project_dir, path)
          @installed_frameworks[:pre_built].uniq!
        elsif path.include?('$BUILT_PRODUCTS_DIR')
          path = path.sub('$BUILT_PRODUCTS_DIR', "#{PODS_ROOT}/.build")
          @installed_frameworks[:build] << File.join(@config.project_dir, path)
          @installed_frameworks[:build].uniq!
        end
      end
    end
  }
  @installed_frameworks
end
lib_search_path_flags() click to toggle source
# File lib/motion/project/cocoapods.rb, line 421
def lib_search_path_flags
  lib_search_paths.map { |p| "-L'#{p}'" }.join(' ')
end
lib_search_paths() click to toggle source
# File lib/motion/project/cocoapods.rb, line 425
def lib_search_paths
  @lib_search_paths ||= begin
    xcconfig = pods_xcconfig_hash
    @lib_search_path_flags = xcconfig['LIBRARY_SEARCH_PATHS'] || ""

    paths = []
    @lib_search_path_flags = @lib_search_path_flags.split(/\s/).map do |path|
      if path =~ /(\$\(inherited\))|(\$\{inherited\})|(\$CONFIGURATION_BUILD_DIR)|(\$PODS_CONFIGURATION_BUILD_DIR)/
        nil
      else
        path = path.gsub(/(\$\(PODS_ROOT\))|(\$\{PODS_ROOT\})/, File.join(@config.project_dir, PODS_ROOT))
        paths << path.gsub('"', '')
      end
    end.compact.join(' ')
    paths
  end

  @lib_search_paths
end
pod(*name_and_version_requirements, &block) click to toggle source
# File lib/motion/project/cocoapods.rb, line 205
def pod(*name_and_version_requirements, &block)
  @podfile.pod(*name_and_version_requirements, &block)
end
pods_frameworks(frameworks) click to toggle source
# File lib/motion/project/cocoapods.rb, line 369
def pods_frameworks(frameworks)
  frameworks = frameworks.dup
  if installed_frameworks[:pre_built]
    installed_frameworks[:pre_built].each do |pre_built|
      frameworks.delete(File.basename(pre_built, ".framework"))
    end
  end

  static_frameworks = []
  case @config.deploy_platform
  when 'MacOSX'
    @config.framework_search_paths.concat(framework_search_paths)
    @config.framework_search_paths.uniq!
    framework_search_paths.each do |framework_search_path|
      frameworks.reject! do |framework|
        path = File.join(framework_search_path, "#{framework}.framework")
        if File.exist?(path)
          @config.embedded_frameworks << path
          @config.embedded_frameworks.uniq!
          true
        else
          false
        end
      end
    end
  when 'iPhoneOS', 'AppleTVOS', 'WatchOS'
    # If we would really specify these as ‘frameworks’ then the linker
    # would not link the archive into the application, because it does not
    # see any references to any of the symbols in the archive. Treating it
    # as a static library (which it is) with `-ObjC` fixes this.
    #
    framework_search_paths.each do |framework_search_path|
      frameworks.reject! do |framework|
        path = File.join(framework_search_path, "#{framework}.framework")
        if File.exist?(path)
          @config.libs << "-ObjC '#{File.join(path, framework)}'"
          @config.libs.uniq!
          static_frameworks << framework
          true
        else
          false
        end
      end
    end
  end

  @config.frameworks.concat(frameworks)
  @config.frameworks.uniq!

  static_frameworks
end
pods_installer() click to toggle source

Installation

# File lib/motion/project/cocoapods.rb, line 226
def pods_installer
  @installer ||= Pod::Installer.new(cp_config.sandbox, @podfile, cp_config.lockfile)
end
pods_libraries() click to toggle source
# File lib/motion/project/cocoapods.rb, line 338
def pods_libraries
  xcconfig = pods_xcconfig_hash
  ldflags = xcconfig['OTHER_LDFLAGS']

  # Get the name of all static libraries that come pre-built with pods
  pre_built_static_libs = lib_search_paths.map do |path|
    Dir[File.join(path, '**/*.a')].map { |f| File.basename(f) }
  end.flatten

  pods_libs = []
  @config.libs.concat(ldflags.scan(/-l"?([^\s"]+)"?/).map { |m|
    lib_name = m[0]
    next if lib_name.nil?
    if lib_name.start_with?('Pods-')
      # For CocoaPods 0.37.x or below. This block is marked as deprecated.
      pods_libs << lib_name
      nil
    elsif pre_built_static_libs.include?("lib#{lib_name}.a")
      "#{lib_search_path_flags} -ObjC -l#{lib_name}"
    elsif File.exist?("/usr/lib/lib#{lib_name}.dylib")
      "/usr/lib/lib#{lib_name}.dylib"
    else
      pods_libs << lib_name
      nil
    end
  }.compact)
  @config.libs.uniq!

  pods_libs
end
pods_xcconfig() click to toggle source
# File lib/motion/project/cocoapods.rb, line 324
def pods_xcconfig
  @pods_xcconfig ||= begin
    path = Pathname.new(@config.project_dir) + SUPPORT_FILES + "Pods-#{TARGET_NAME}.release.xcconfig"
    Xcodeproj::Config.new(path) if path.exist?
  end
  @pods_xcconfig
end
pods_xcconfig_hash() click to toggle source
# File lib/motion/project/cocoapods.rb, line 332
def pods_xcconfig_hash
  if xcconfig = pods_xcconfig
    xcconfig.to_hash
  end
end
post_install(&block) click to toggle source
# File lib/motion/project/cocoapods.rb, line 214
def post_install(&block)
  @podfile.post_install(&block)
end
resources() click to toggle source

Do not copy `.framework` bundles, these should be handled through RM's `embedded_frameworks` config attribute.

# File lib/motion/project/cocoapods.rb, line 524
def resources
  resources = []
  script = Pathname.new(@config.project_dir) + SUPPORT_FILES + "Pods-#{TARGET_NAME}-resources.sh"
  return resources unless File.exist?(script)
  File.open(script) { |f|
    f.each_line do |line|
      if matched = line.match(/install_resource\s+(.*)/)
        path = (matched[1].strip)[1..-2]
        if path.start_with?('${PODS_ROOT}')
          path = path.sub('${PODS_ROOT}/', '')
        end
        if path.include?("$PODS_CONFIGURATION_BUILD_DIR")
          path = File.join(".build", File.basename(path))
        end
        unless File.extname(path) == '.framework'
          resources << Pathname.new(@config.project_dir) + PODS_ROOT + path
        end
      end
    end
  }
  resources.uniq
end
resources_dir() click to toggle source
# File lib/motion/project/cocoapods.rb, line 547
def resources_dir
  Pathname.new(@config.project_dir) + PODS_ROOT + 'Resources'
end
source(source) click to toggle source

DSL

# File lib/motion/project/cocoapods.rb, line 201
def source(source)
  @podfile.source(source)
end
static_frameworks_paths(frameworks) click to toggle source
# File lib/motion/project/cocoapods.rb, line 510
def static_frameworks_paths(frameworks)
  paths = []
  framework_search_paths.each do |framework_search_path|
    paths += Dir.glob("#{framework_search_path}/*.framework")
  end
  paths.keep_if { |path|
    frameworks.include?(File.basename(path, ".framework"))
  }
  paths
end
use_frameworks!(flag = true) click to toggle source
# File lib/motion/project/cocoapods.rb, line 218
def use_frameworks!(flag = true)
  @use_frameworks = flag
  @podfile.use_frameworks!(flag)
end