CocoaPods/Core

View on GitHub
lib/cocoapods-core/specification/dsl.rb

Summary

Maintainability
C
7 hrs
Test Coverage
require 'cocoapods-core/specification/dsl/attribute_support'
require 'cocoapods-core/specification/dsl/attribute'
require 'cocoapods-core/specification/dsl/platform_proxy'

module Pod
  class Specification
    #- NOTE ------------------------------------------------------------------#
    # The order of the methods defined in this file and the order of the
    # methods is relevant for the documentation generated on the
    # CocoaPods/cocoapods.github.com repository.
    #-------------------------------------------------------------------------#

    # A specification describes a version of Pod library. It includes details
    # about where the source should be fetched from, what files to use, the
    # build settings to apply, and other general metadata such as its name,
    # version, and description.
    #
    # ---
    #
    # A stub specification file can be generated by the [pod spec
    # create](http://guides.cocoapods.org/terminal/commands.html#pod_spec_create) command.
    #
    # ---
    #
    # The specification DSL provides great flexibility and dynamism. Moreover,
    # the DSL adopts the
    # [convention over configuration](http://en.wikipedia.org/wiki/Convention_over_configuration)
    # and thus it can be very simple:
    #
    #     Pod::Spec.new do |spec|
    #       spec.name         = 'Reachability'
    #       spec.version      = '3.1.0'
    #       spec.license      = { :type => 'BSD' }
    #       spec.homepage     = 'https://github.com/tonymillion/Reachability'
    #       spec.authors      = { 'Tony Million' => 'tonymillion@gmail.com' }
    #       spec.summary      = 'ARC and GCD Compatible Reachability Class for iOS and OS X.'
    #       spec.source       = { :git => 'https://github.com/tonymillion/Reachability.git', :tag => 'v3.1.0' }
    #       spec.source_files = 'Reachability.{h,m}'
    #       spec.framework    = 'SystemConfiguration'
    #     end
    #
    # Or it can be quite detailed:
    #
    #     Pod::Spec.new do |spec|
    #       spec.name          = 'Reachability'
    #       spec.version       = '3.1.0'
    #       spec.license       = { :type => 'BSD' }
    #       spec.homepage      = 'https://github.com/tonymillion/Reachability'
    #       spec.authors       = { 'Tony Million' => 'tonymillion@gmail.com' }
    #       spec.summary       = 'ARC and GCD Compatible Reachability Class for iOS and OS X.'
    #       spec.source        = { :git => 'https://github.com/tonymillion/Reachability.git', :tag => 'v3.1.0' }
    #       spec.module_name   = 'Rich'
    #       spec.swift_version = '4.0'
    #
    #       spec.ios.deployment_target  = '9.0'
    #       spec.osx.deployment_target  = '10.10'
    #
    #       spec.source_files       = 'Reachability/common/*.swift'
    #       spec.ios.source_files   = 'Reachability/ios/*.swift', 'Reachability/extensions/*.swift'
    #       spec.osx.source_files   = 'Reachability/osx/*.swift'
    #
    #       spec.framework      = 'SystemConfiguration'
    #       spec.ios.framework  = 'UIKit'
    #       spec.osx.framework  = 'AppKit'
    #
    #       spec.dependency 'SomeOtherPod'
    #     end
    #
    module DSL
      extend Pod::Specification::DSL::AttributeSupport

      # Deprecations must be required after include AttributeSupport
      require 'cocoapods-core/specification/dsl/deprecations'

      #-----------------------------------------------------------------------#

      # @!group Root specification
      #
      #   A ‘root’ specification stores the information about the specific
      #   version of a library.
      #
      #   The attributes in this group can only be written to on the ‘root’
      #   specification, **not** on the ‘sub-specifications’.
      #
      #   ---
      #
      #   The attributes listed in this group are the only one which are
      #   required by a podspec.
      #
      #   The attributes of the other groups are offered to refine the podspec
      #   and follow a convention over configuration approach.  A root
      #   specification can describe these attributes either directly of
      #   through ‘[sub-specifications](#subspec)’.

      #-----------------------------------------------------------------------#

      # @!method name=(name)
      #
      #   The name of the Pod.
      #
      #   @example
      #
      #     spec.name = 'AFNetworking'
      #
      #   @param  [String] name
      #           the name of the pod.
      #
      attribute :name,
                :required => true,
                :inherited => false,
                :multi_platform => false

      #------------------#

      # @!method version=(version)
      #
      #   The version of the Pod. CocoaPods follows
      #   [semantic versioning](http://semver.org).
      #
      #   @example
      #
      #     spec.version = '0.0.1'
      #
      #   @param  [String] version
      #           the version of the Pod.
      #
      root_attribute :version,
                     :required => true

      #------------------#

      # @!method swift_versions=(version)
      #
      #   The versions of Swift that the specification supports. A version of '4' will be treated as
      #   '4.0' by CocoaPods and not '4.1' or '4.2'.
      #
      #   **Note** The Swift compiler mostly accepts major versions and sometimes will honor minor versions.
      #   While CocoaPods allows specifying a minor or patch version it might not be honored fully by the Swift compiler.
      #
      #   @example
      #
      #     spec.swift_versions = ['3.0']
      #
      #   @example
      #
      #     spec.swift_versions = ['3.0', '4.0', '4.2']
      #
      #   @example
      #
      #     spec.swift_version = '3.0'
      #
      #   @example
      #
      #     spec.swift_version = '3.0', '4.0'
      #
      #   @param  [String, Array<String>] swift_versions
      #
      root_attribute :swift_versions,
                     :container => Array,
                     :singularize => true

      #-----------------------------------------------------------------------#

      # @!method cocoapods_version=(cocoapods_version)
      #
      #   The version of CocoaPods that the specification supports.
      #
      #   @example
      #
      #     spec.cocoapods_version = '>= 0.36'
      #
      #   @param  [String] cocoapods_version
      #           the CocoaPods version that the specification supports.
      #           CocoaPods follows [semantic versioning](http://semver.org).
      #
      root_attribute :cocoapods_version

      #------------------#

      # @!method authors=(authors)
      #
      #   The name and email addresses of the library maintainers, not the
      #   Podspec maintainer.
      #
      #   @example
      #
      #     spec.author = 'Darth Vader'
      #
      #   @example
      #
      #     spec.authors = 'Darth Vader', 'Wookiee'
      #
      #   @example
      #
      #     spec.authors = { 'Darth Vader' => 'darthvader@darkside.com',
      #                      'Wookiee'     => 'wookiee@aggrrttaaggrrt.com' }
      #
      #   @param  [String, Hash{String=>String}] authors
      #           the list of the authors of the library and their emails.
      #
      root_attribute :authors,
                     :types       => [String, Array, Hash],
                     :container   => Hash,
                     :required    => true,
                     :singularize => true

      #------------------#

      # @!method social_media_url=(social_media_url)
      #
      #   The URL for the social media contact of the Pod, CocoaPods web
      #   services can use this.
      #
      #   For example, the @CocoaPodsFeed notifications will include the
      #   Twitter handle (shortening the description) if the URL is relative to
      #   Twitter. This does **not** necessarily have to be a Twitter URL, but
      #   only those are included in the Twitter @CocoaPodsFeed notifications.
      #
      #   @example
      #
      #     spec.social_media_url = 'https://twitter.com/cocoapods'
      #
      #   @example
      #
      #     spec.social_media_url = 'https://groups.google.com/forum/#!forum/cocoapods'
      #
      #   @param  [String] social_media_url
      #           the social media URL.
      #
      root_attribute :social_media_url

      #------------------#

      # The keys accepted by the license attribute.
      #
      LICENSE_KEYS = [:type, :file, :text].freeze

      # @!method license=(license)
      #
      #   The license of the Pod.
      #
      #   ---
      #
      #   Unless the source contains a file named `LICENSE.*` or `LICENCE.*`,
      #   the path of the license file **or** the integral text of the notice
      #   commonly used for the license type must be specified.
      #   If a license file is specified, it either must be without a file
      #   extensions or be one of `txt`, `md`, or `markdown`.
      #
      #   This information is used by CocoaPods to generate acknowledgement
      #   files (markdown and plist) which can be used in the acknowledgements
      #   section of the final application.
      #
      #   @example
      #
      #     spec.license = 'MIT'
      #
      #   @example
      #
      #     spec.license = { :type => 'MIT', :file => 'MIT-LICENSE.txt' }
      #
      #   @example
      #
      #     spec.license = { :type => 'MIT', :text => <<-LICENSE
      #                        Copyright 2012
      #                        Permission is granted to...
      #                      LICENSE
      #                    }
      #
      #   @param  [String] license
      #           The type of the license
      #
      #   @overload license=(license)
      #     @param  [String, Hash{Symbol=>String}] license
      #     @option license [String] :type license type
      #     @option license [String] :file file containing full license text. Supports txt, md, and markdown
      #     @option license [String] :text full license text
      #
      root_attribute :license,
                     :container => Hash,
                     :keys      => LICENSE_KEYS,
                     :required  => true

      #------------------#

      # @!method homepage=(homepage)
      #
      #   The URL of the homepage of the Pod.
      #
      #   @example
      #
      #     spec.homepage = 'http://www.example.com'
      #
      #   @param  [String] homepage
      #           the URL of the homepage of the Pod.
      #
      root_attribute :homepage,
                     :required => true

      #------------------#

      # @!method readme=(readme)
      #
      #   The URL for the README markdown file for this pod version.
      #
      #   @example
      #
      #     spec.readme = 'https://www.example.com/Pod-1.5-README.md'
      #
      #   @param  [String] readme
      #           the readme markdown URL.
      #
      root_attribute :readme

      #------------------#

      # @!method changelog=(changelog)
      #
      #   The URL for the CHANGELOG markdown file for this pod version.
      #
      #   @example
      #
      #     spec.changelog = 'https://www.example.com/Pod-1.5-CHANGELOG.md'
      #
      #   @param  [String] changelog
      #           the changelog markdown URL.
      #
      root_attribute :changelog

      #------------------#

      # The keys accepted by the hash of the source attribute.
      #
      SOURCE_KEYS = {
        :git  => [:tag, :branch, :commit, :submodules].freeze,
        :svn  => [:folder, :tag, :revision].freeze,
        :hg   => [:revision].freeze,
        :http => [:flatten, :type, :sha256, :sha1, :headers].freeze,
      }.freeze

      # @!method source=(source)
      #
      #   The location from where the library should be retrieved.
      #
      #   @example Specifying a Git source with a tag. This is how most OSS Podspecs work.
      #
      #     spec.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git',
      #                     :tag => spec.version.to_s }
      #
      #   @example Using a tag prefixed with 'v' and submodules.
      #
      #     spec.source = { :git => 'https://github.com/typhoon-framework/Typhoon.git',
      #                     :tag => "v#{spec.version}", :submodules => true }
      #
      #   @example Using Subversion with a tag.
      #
      #     spec.source = { :svn => 'http://svn.code.sf.net/p/polyclipping/code', :tag => '4.8.8' }
      #
      #   @example Using Mercurial with the same revision as the spec's semantic version string.
      #
      #     spec.source = { :hg => 'https://bitbucket.org/dcutting/hyperbek', :revision => "#{s.version}" }
      #
      #   @example Using HTTP to download a compressed file of the code. It supports zip, tgz, bz2, txz and tar.
      #
      #     spec.source = { :http => 'http://dev.wechatapp.com/download/sdk/WeChat_SDK_iOS_en.zip' }
      #
      #   @example Using HTTP to download a file using a hash to verify the download. It supports sha1 and sha256.
      #
      #     spec.source = { :http => 'http://dev.wechatapp.com/download/sdk/WeChat_SDK_iOS_en.zip',
      #                     :sha1 => '7e21857fe11a511f472cfd7cfa2d979bd7ab7d96' }
      #
      #
      #   @overload source=(git)
      #     @param  [Hash] git
      #     @option git [String] :git git source URI
      #     @option git [String] :tag version tag
      #     @option git [Boolean] :submodules Whether to checkout submodules
      #     @option git [String] :branch branch name
      #     @option git [String] :commit commit hash
      #
      #   @overload source=(svn)
      #     @param  [Hash] svn
      #     @option svn [String] :svn svn source URI
      #     @option svn [String] :tag version tag
      #     @option svn [String] :folder folder
      #     @option svn [String] :revision revision
      #
      #   @overload source=(hg)
      #     @param  [Hash] hg
      #     @option hg [String] :hg mercurial source URI
      #     @option hg [String] :revision revision
      #
      #   @overload source=(http)
      #     @param  [Hash] http
      #     @option http [String] :http compressed source URL
      #     @option http [String] :type file type. Supports zip, tgz, bz2, txz and tar
      #     @option http [String] :sha1 SHA hash. Supports SHA1 and SHA256
      #
      root_attribute :source,
                     :container => Hash,
                     :keys      => SOURCE_KEYS,
                     :required  => true

      #------------------#

      # @!method summary=(summary)
      #
      #   A short (maximum 140 characters) description of the Pod.
      #
      #   ---
      #
      #   The description should be short, yet informative. It represents the
      #   tag line of the Pod and there is no need to specify that a Pod is a
      #   library (they always are).
      #
      #   The summary is expected to be properly capitalised and containing the
      #   correct punctuation.
      #
      #   @example
      #
      #     spec.summary = 'Computes the meaning of life.'
      #
      #   @param  [String] summary
      #           A short description of the Pod.
      #
      root_attribute :summary,
                     :required => true

      #------------------#

      # @!method description=(description)
      #
      #   A description of the Pod more detailed than the summary.
      #
      #   @example
      #
      #     spec.description = <<-DESC
      #                          Computes the meaning of life.
      #                          Features:
      #                          1. Is self aware
      #                          ...
      #                          42. Likes candies.
      #                        DESC
      #
      #   @param  [String] description
      #           A longer description of the Pod.
      #
      root_attribute :description

      #------------------#

      # @!method screenshots=(screenshots)
      #
      #   A list of URLs to images showcasing the Pod. Intended for UI oriented
      #   libraries. CocoaPods recommends the usage of the `gif` format.
      #
      #   @example
      #
      #     spec.screenshot  = 'http://dl.dropbox.com/u/378729/MBProgressHUD/1.png'
      #
      #   @example
      #
      #     spec.screenshots = [ 'http://dl.dropbox.com/u/378729/MBProgressHUD/1.png',
      #                          'http://dl.dropbox.com/u/378729/MBProgressHUD/2.png' ]
      #
      #   @param  [String] screenshots
      #           An URL for the screenshot of the Pod.
      #
      root_attribute :screenshots,
                     :singularize    => true,
                     :container      => Array

      #------------------#

      # @!method documentation_url=(documentation_url)
      #
      #   An optional URL for the documentation of the Pod which will be honoured by
      #   CocoaPods web properties. Leaving it blank will default to a CocoaDocs
      #   generated URL for your library.
      #
      #   @example
      #
      #     spec.documentation_url = 'http://www.example.com/docs.html'
      #
      #   @param  [String] documentation_url
      #           The link of the web documentation of the Pod.
      #
      root_attribute :documentation_url

      #------------------#

      # @!method prepare_command=(command)
      #
      #   A bash script that will be executed after the Pod is downloaded. This
      #   command can be used to create, delete and modify any file downloaded
      #   and will be ran before any paths for other file attributes of the
      #   specification are collected.
      #
      #   This command is executed before the Pod is cleaned and before the
      #   Pods project is created. The working directory is the root of the
      #   Pod.
      #
      #   If the pod is installed with the `:path` option this command will not
      #   be executed.
      #
      #   @example
      #
      #     spec.prepare_command = 'ruby build_files.rb'
      #
      #   @example
      #
      #     spec.prepare_command = <<-CMD
      #                             sed -i 's/MyNameSpacedHeader/Header/g' ./**/*.h
      #                             sed -i 's/MyNameOtherSpacedHeader/OtherHeader/g' ./**/*.h
      #                        CMD
      #
      #   @param  [String] command
      #           the prepare command of the pod.
      #
      root_attribute :prepare_command

      #------------------#

      # @!method static_framework=(flag)
      #
      #   Indicates, that if use_frameworks! is specified, the
      #   pod should include a static library framework.
      #
      #   @example
      #
      #     spec.static_framework = true
      #
      #   @param [Boolean] flag
      #          Indicates, that if use_frameworks! is specified, the
      #          pod should include a static library framework.
      #
      root_attribute :static_framework,
                     :types => [TrueClass, FalseClass],
                     :default_value => false

      #------------------#

      # @!method deprecated=(flag)
      #
      #   Whether the library has been deprecated.
      #
      #   @example
      #
      #     spec.deprecated = true
      #
      #   @param [Boolean] flag
      #          whether the library has been deprecated.
      #
      root_attribute :deprecated,
                     :types => [TrueClass, FalseClass],
                     :default_value => false

      # @!method deprecated_in_favor_of=(deprecated_in_favor_of)
      #
      #   The name of the Pod that this one has been deprecated in favor of.
      #
      #   @example
      #
      #     spec.deprecated_in_favor_of = 'NewMoreAwesomePod'
      #
      #   @param  [String] deprecated_in_favor_of
      #           the name of the Pod that this one has been deprecated in
      #           favor of.
      #
      root_attribute :deprecated_in_favor_of

      #-----------------------------------------------------------------------#

      # @!group Platform
      #
      #   A specification should indicate the platform and the correspondent
      #   deployment targets on which the library is supported.
      #
      #   If not defined in a subspec the attributes of this group inherit the
      #   value of the parent.

      #-----------------------------------------------------------------------#

      # The names of the platforms supported by the specification class.
      #
      PLATFORMS = [:osx, :ios, :tvos, :visionos, :watchos].freeze

      # @todo This currently is not used in the Ruby DSL.
      #
      attribute :platforms,
                :container => Hash,
                :keys => PLATFORMS,
                :multi_platform => false,
                :inherited => true

      # The platform on which this Pod is supported. Leaving this blank
      # means the Pod is supported on all platforms. When supporting multiple
      # platforms you should use deployment_target below instead.
      #
      # @example
      #
      #   spec.platform = :osx, '10.8'
      #
      # @example
      #
      #   spec.platform = :ios
      #
      # @example
      #
      #   spec.platform = :osx
      #
      # @param  [Array<Symbol, String>] args
      #         A tuple where the first value is the name of the platform,
      #         (either `:ios` or `:osx`) and the second is the deployment
      #         target.
      #
      def platform=(args)
        name, deployment_target = args
        name = :osx if name.to_s == 'macos'
        attributes_hash['platforms'] = if name
                                         { name.to_s => deployment_target }
                                       else
                                         {}
                                       end
      end

      #------------------#

      # The minimum deployment targets of the supported platforms.
      #
      # As opposed to the `platform` attribute, the `deployment_target`
      # attribute allows to specify multiple platforms on which this pod
      # is supported — specifying a different deployment target for each.
      #
      # @example
      #
      #   spec.ios.deployment_target = '6.0'
      #
      # @example
      #
      #   spec.osx.deployment_target = '10.8'
      #
      # @param    [String] _args
      #           The deployment target of the platform.
      #
      def deployment_target=(*_args)
        raise Informative, 'The deployment target can be declared only per ' \
          'platform.'
      end

      #-----------------------------------------------------------------------#

      # @!group Build settings
      #
      #   In this group are listed the attributes related to the configuration
      #   of the build environment that should be used to build the library.
      #
      #   If not defined in a subspec the attributes of this group inherit the
      #   value of the parent.

      #-----------------------------------------------------------------------#

      # @todo This currently is not used in the Ruby DSL.
      #
      attribute :dependencies,
                :container => Hash,
                :inherited => true

      # Any dependency on other Pods or to a ‘sub-specification’.
      #
      # ---
      #
      # Dependencies can specify versions requirements. The use of the optimistic
      # version indicator `~>` is recommended because it provides good
      # control over the version without being too restrictive. For example,
      # `~> 1.0.1` is equivalent to `>= 1.0.1` combined with `< 1.1`. Similarly,
      # `~> 1.0` will match `1.0`, `1.0.1`, `1.1`, but will not upgrade to `2.0`.
      #
      # Pods with overly restrictive dependencies limit their compatibility with
      # other Pods.
      #
      # @example
      #   spec.dependency 'AFNetworking', '~> 1.0'
      #
      # @example
      #   spec.dependency 'AFNetworking', '~> 1.0', :configurations => ['Debug']
      #
      # @example
      #   spec.dependency 'AFNetworking', '~> 1.0', :configurations => :debug
      #
      # @example
      #   spec.dependency 'RestKit/CoreData', '~> 0.20.0'
      #
      # @example
      #   spec.ios.dependency 'MBProgressHUD', '~> 0.5'
      #
      def dependency(*args)
        name, *version_requirements = args
        if name == self.name
          raise Informative, "A specification can't require itself as a " \
            'subspec'
        end
        if @parent
          composed_name = ''
          @parent.name.split('/').each do |component|
            composed_name << component
            if name == composed_name
              raise Informative, "A subspec can't require one of its " \
                'parents specifications'
            else
              composed_name << '/'
            end
          end
        end

        configurations_option = version_requirements.find { |option| option.is_a?(Hash) && option.key?(:configurations) }
        whitelisted_configurations = if configurations_option
                                       version_requirements.delete(configurations_option)
                                       Array(configurations_option.delete(:configurations)).map { |c| c.to_s.downcase }
                                     end

        dependency_options = version_requirements.reject { |req| req.is_a?(String) }
        dependency_options.each do |dependency_option|
          if dependency_option.is_a?(Hash)
            if !dependency_option[:path].nil?
              raise Informative, 'Podspecs cannot specify the source of dependencies. The `:path` option is not supported.'\
                                 ' `:path` can be used in the Podfile instead to override global dependencies.'
            elsif !dependency_option[:git].nil?
              raise Informative, 'Podspecs cannot specify the source of dependencies. The `:git` option is not supported.'\
                                 ' `:git` can be used in the Podfile instead to override global dependencies.'
            end
          end

          raise Informative, "Unsupported version requirements. #{version_requirements.inspect} is not valid."
        end

        attributes_hash['dependencies'] ||= {}
        attributes_hash['dependencies'][name] = version_requirements

        unless whitelisted_configurations.nil?
          if (extras = whitelisted_configurations - %w(debug release)) && !extras.empty?
            raise Informative, "Only `Debug` & `Release` are allowed under configurations for dependency on `#{name}`. " \
              "Found #{extras.map { |configuration| "`#{configuration}`" }.to_sentence}."
          end
          attributes_hash['configuration_pod_whitelist'] ||= {}
          attributes_hash['configuration_pod_whitelist'][name] = whitelisted_configurations
        end
      end

      def dependency=(args)
        joined = args.join('\', \'')
        arguments = "\'#{joined}\'"
        raise Informative, "Cannot assign value to `dependency`. Did you mean: `dependency #{arguments}`?"
      end

      #------------------#

      # @!method info_plist=(info_plist)
      #
      #   Key-Value pairs to add to the generated `Info.plist`.
      #
      #   The values will be merged with the default values that
      #   CocoaPods generates, overriding any duplicates.
      #
      #   For library specs, the values will be merged into the generated Info.plist
      #   for libraries that are integrated using frameworks. It will have no effect
      #   for static libraries.
      #
      #   Subspecs (other than app and test specs) are not supported.
      #
      #   For app specs, the values will be merged into the application host's `Info.plist`.
      #
      #   For test specs, the values will be merged into the test bundle's `Info.plist`.
      #
      #   @example
      #
      #     spec.info_plist = {
      #       'CFBundleIdentifier' => 'com.myorg.MyLib',
      #       'MY_VAR' => 'SOME_VALUE'
      #     }
      #
      #   @param  [Hash] info_plist
      #           The Info.plist values for the Pod.
      #
      attribute :info_plist,
                :container => Hash,
                :inherited => false

      #------------------#

      # @!method requires_arc=(flag)
      #
      #   `requires_arc` allows you to specify which source_files use ARC.
      #   This can either be the files which support ARC, or true to indicate
      #   all of the source_files use ARC.
      #
      #   Files which do not use ARC will have the `-fno-objc-arc` compiler
      #   flag.
      #
      #   The default value of this attribute is `true`.
      #
      #   @example
      #
      #     spec.requires_arc = false
      #
      #   @example
      #
      #     spec.requires_arc = 'Classes/Arc'
      #
      #   @example
      #
      #     spec.requires_arc = ['Classes/*ARC.m', 'Classes/ARC.mm']
      #
      #   @param [Bool, String, Array<String>] flag
      #          whether the source files require ARC.
      #
      attribute :requires_arc,
                :types => [TrueClass, FalseClass, String, Array],
                :file_patterns => true,
                :default_value => true,
                :inherited => true

      #------------------#

      # @!method frameworks=(*frameworks)
      #
      #   A list of system frameworks that the user’s target needs to link
      #   against.
      #
      #   @example
      #
      #     spec.ios.framework = 'CFNetwork'
      #
      #   @example
      #
      #     spec.frameworks = 'QuartzCore', 'CoreData'
      #
      #   @param  [String, Array<String>] frameworks
      #           A list of framework names.
      #
      attribute :frameworks,
                :container   => Array,
                :singularize => true,
                :inherited => true

      #------------------#

      # @!method weak_frameworks=(*frameworks)
      #
      #   A list of frameworks that the user’s target needs to **weakly** link
      #   against.
      #
      #   @example
      #
      #     spec.weak_framework = 'Twitter'
      #
      #   @example
      #
      #     spec.weak_frameworks = 'Twitter', 'SafariServices'
      #
      #   @param  [String, Array<String>] weak_frameworks
      #           A list of frameworks names.
      #
      attribute :weak_frameworks,
                :container   => Array,
                :singularize => true,
                :inherited => true

      #------------------#

      # @!method libraries=(*libraries)
      #
      #   A list of system libraries that the user’s target (application) needs to
      #   link against.
      #
      #   @example
      #
      #     spec.ios.library = 'xml2'
      #
      #   @example
      #
      #     spec.libraries = 'xml2', 'z'
      #
      #   @param  [String, Array<String>] libraries
      #           A list of library names.
      #
      attribute :libraries,
                :container   => Array,
                :singularize => true,
                :inherited => true

      #------------------#

      # @!method compiler_flags=(flags)
      #
      #   A list of flags which should be passed to the compiler.
      #
      #   @example
      #
      #     spec.compiler_flags = '-DOS_OBJECT_USE_OBJC=0', '-Wno-format'
      #
      #   @param  [String, Array<String>] flags
      #           A list of flags.
      #
      attribute :compiler_flags,
                :container   => Array,
                :singularize => true,
                :inherited => true

      #------------------#

      # @!method pod_target_xcconfig=(value)
      #
      #   Any flag to add to the final __private__ pod target xcconfig file.
      #
      #   @example
      #
      #     spec.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '-lObjC' }
      #
      #   @param  [Hash{String => String}] value
      #           Key-value pairs representing build settings.
      #
      attribute :pod_target_xcconfig,
                :container => Hash,
                :inherited => true

      # @!method user_target_xcconfig=(value)
      #
      #   Specifies flags to add to the final aggregate target xcconfig file,
      #   which propagates to non-overridden and inheriting build settings to
      #   the integrated user targets.
      #
      #   ---
      #
      #   This attribute is __not recommended__ as Pods should not pollute the
      #   build settings of the user project and this can cause conflicts.
      #
      #   Multiple definitions for build settings that take multiple values
      #   will be merged. The user is warned on conflicting definitions for
      #   custom build settings and build settings that take only one value.
      #
      #   Typically clang compiler flags or precompiler macro definitions go
      #   in here if they are required when importing the pod in the user
      #   target. Note that, this influences not only the compiler view of the
      #   public interface of your pod, but also all other integrated pods
      #   alongside to yours. You should always prefer [`pod_target_xcconfig`](
      #   http://guides.cocoapods.org/syntax/podspec.html#pod_target_xcconfig),
      #   which can contain the same settings, but only influence the
      #   toolchain when compiling your pod target.
      #
      #   @example
      #
      #     spec.user_target_xcconfig = { 'MY_SUBSPEC' => 'YES' }
      #
      #   @param  [Hash{String => String}] value
      #           Key-value pairs representing build settings.
      #
      attribute :user_target_xcconfig,
                :container => Hash,
                :inherited => true

      #------------------#

      # @!method prefix_header_contents=(content)
      #
      #   Any content to inject in the prefix header of the pod project.
      #
      #   ---
      #
      #   This attribute is __not recommended__ as Pods should not pollute the
      #   prefix header of other libraries or of the user project.
      #
      #   @example
      #
      #     spec.prefix_header_contents = '#import <UIKit/UIKit.h>'
      #
      #   @example
      #
      #     spec.prefix_header_contents = '#import <UIKit/UIKit.h>', '#import <Foundation/Foundation.h>'
      #
      #   @param  [String] content
      #           The contents of the prefix header.
      #
      attribute :prefix_header_contents,
                :types => [Array, String],
                :inherited => true

      #------------------#

      # @!method prefix_header_file=(path)
      #
      #   A path to a prefix header file to inject in the prefix header of the
      #   pod project.
      #   `false` indicates that the default CocoaPods prefix header should not
      #   be generated.
      #   `true` is the default and indicates that the default CocoaPods prefix
      #   header should be generated.
      #
      #   ---
      #
      #   The file path options is __not recommended__ as Pods should not
      #   pollute the prefix header of other libraries or of the user project.
      #
      #
      #   @example
      #
      #     spec.prefix_header_file = 'iphone/include/prefix.pch'
      #
      #   @example
      #
      #     spec.prefix_header_file = false
      #
      #   @param  [Bool, String] path
      #           The path to the prefix header file or whether to disable
      #           prefix_header generation.
      #
      attribute :prefix_header_file,
                :types => [TrueClass, FalseClass, String],
                :inherited => true

      #------------------#

      # @!method module_name=(name)
      #
      #   The name to use for the framework / clang module which
      #   will be generated for this specification instead of the
      #   default (header_dir if set, otherwise the specification
      #   name).
      #
      #   @example
      #
      #     spec.module_name = 'Three20'
      #
      #   @param  [String] name
      #           the module name.
      #
      root_attribute :module_name

      #------------------#

      # @!method header_dir=(dir)
      #
      #   The directory where to store the headers files so they don't break
      #   includes.
      #
      #   @example
      #
      #     spec.header_dir = 'Three20Core'
      #
      #   @param  [String] dir
      #           the headers directory.
      #
      attribute :header_dir,
                :inherited => true

      #------------------#

      # @!method header_mappings_dir=(dir)
      #
      #   A directory from where to preserve the folder structure for the
      #   headers files. If not provided the headers files are flattened.
      #
      #   @example
      #
      #     spec.header_mappings_dir = 'src/include'
      #
      #   @param  [String] dir
      #           the directory from where to preserve the headers namespacing.
      #
      attribute :header_mappings_dir,
                :inherited => true

      #------------------#

      SCRIPT_PHASE_REQUIRED_KEYS = [:name, :script].freeze

      SCRIPT_PHASE_OPTIONAL_KEYS = [:shell_path, :input_files, :output_files, :input_file_lists, :output_file_lists,
                                    :show_env_vars_in_log, :execution_position, :dependency_file, :always_out_of_date].freeze

      EXECUTION_POSITION_KEYS = [:before_compile, :after_compile, :before_headers, :after_headers, :any].freeze

      ALL_SCRIPT_PHASE_KEYS = (SCRIPT_PHASE_REQUIRED_KEYS + SCRIPT_PHASE_OPTIONAL_KEYS).freeze

      # @!method script_phases=(*script_phases)
      #
      #   This attribute allows to define a script phase to execute as part of compilation of the Pod.
      #   Unlike a prepare command, script phases execute as part of `xcodebuild` they can also utilize all environment
      #   variables that are set during compilation.
      #
      #   A Pod can provide multiple script phases to execute and they will be added in the order they were
      #   declared and after taking into consideration their execution position setting.
      #
      #   **Note** In order to provide visibility and awareness of the contents of all script phases,
      #   a warning will be presented to the user upon installing your pod if it includes any script phases.
      #
      #   @example
      #
      #     spec.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"' }
      #
      #   @example
      #
      #     spec.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :before_compile }
      #
      #   @example
      #
      #     spec.script_phase = { :name => 'Hello World', :script => 'puts "Hello World"', :shell_path => '/usr/bin/ruby' }
      #
      #   @example
      #
      #     spec.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"',
      #       :input_files => ['/path/to/input_file.txt'], :output_files => ['/path/to/output_file.txt']
      #     }
      #
      #   @example
      #
      #     spec.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"',
      #       :input_file_lists => ['/path/to/input_files.xcfilelist'], :output_file_lists => ['/path/to/output_files.xcfilelist']
      #     }
      #
      #   @example
      #
      #     spec.script_phases = [
      #         { :name => 'Hello World', :script => 'echo "Hello World"' },
      #         { :name => 'Hello Ruby World', :script => 'puts "Hello World"', :shell_path => '/usr/bin/ruby' },
      #       ]
      #
      #   @param  [Array<Hash{Symbol=>String}>] script_phases
      #           An array of hashes where each hash represents a single script phase.
      #
      attribute :script_phases,
                :types => [Hash],
                :container => Array,
                :singularize => true

      #-----------------------------------------------------------------------#

      # @!group File patterns
      #
      #   Podspecs should be located at the **root** of the repository, and paths
      #   to files should be specified **relative** to the root of the repository
      #   as well. File patterns do not support traversing the parent directory ( `..` ).
      #   File patterns may contain the following wildcard patterns:
      #
      #   ---
      #
      #   ### Pattern: *
      #
      #   Matches any file. Can be restricted by other values in the glob.
      #
      #   * `*` will match all files
      #   * `c*` will match all files beginning with `c`
      #   * `*c` will match all files ending with `c`
      #   * `*c*` will match all files that have `c` in them (including at the
      #     beginning or end)
      #
      #   Equivalent to `/.*/x` in regexp.
      #
      #   **Note** this will not match Unix-like hidden files (dotfiles). In
      #   order to include those in the match results, you must use something
      #   like `{*,.*}`.
      #
      #   ---
      #
      #   ### Pattern: **
      #
      #   Matches directories recursively.
      #
      #   ---
      #
      #   ### Pattern: ?
      #
      #   Matches any one character. Equivalent to `/.{1}/` in regexp.
      #
      #   ---
      #
      #   ### Pattern: [set]
      #
      #   Matches any one character in set.
      #
      #   Behaves exactly like character sets in Regexp, including set negation
      #   (`[^a-z]`).
      #
      #   ---
      #
      #   ### Pattern: {p,q}
      #
      #   Matches either literal `p` or literal `q`.
      #
      #   Matching literals may be more than one character in length. More than
      #   two literals may be specified.
      #
      #   Equivalent to pattern alternation in regexp.
      #
      #   ---
      #
      #   ### Pattern: \
      #
      #   Escapes the next meta-character.
      #
      #   ---
      #
      #   ### Examples
      #
      #   Consider these to be evaluated in the source root of
      #   [JSONKit](https://github.com/johnezang/JSONKit).
      #
      #       "JSONKit.?"    #=> ["JSONKit.h", "JSONKit.m"]
      #       "*.[a-z][a-z]" #=> ["CHANGELOG.md", "README.md"]
      #       "*.[^m]*"      #=> ["JSONKit.h"]
      #       "*.{h,m}"      #=> ["JSONKit.h", "JSONKit.m"]
      #       "*"            #=> ["CHANGELOG.md", "JSONKit.h", "JSONKit.m", "README.md"]

      #-----------------------------------------------------------------------#

      # @!method source_files=(source_files)
      #
      #   The source files of the Pod.
      #
      #   @example
      #
      #     spec.source_files = 'Classes/**/*.{h,m}'
      #
      #   @example
      #
      #     spec.source_files = 'Classes/**/*.{h,m}', 'More_Classes/**/*.{h,m}'
      #
      #   @param  [String, Array<String>] source_files
      #           the source files of the Pod.
      #
      attribute :source_files,
                :container     => Array,
                :file_patterns => true

      #------------------#

      # @!method public_header_files=(public_header_files)
      #
      #   A list of file patterns that should be used as public headers.
      #
      #   ---
      #
      #   These patterns are matched against the source files to include headers
      #   that will be exposed to the user’s project and
      #   from which documentation will be generated. When the library is built,
      #   these headers will appear in the build directory. If no public headers
      #   are specified then **all** the headers in source_files are considered
      #   public.
      #
      #   @example
      #
      #     spec.public_header_files = 'Headers/Public/*.h'
      #
      #   @param  [String, Array<String>] public_header_files
      #           the public headers of the Pod.
      #
      attribute :public_header_files,
                :container => Array,
                :file_patterns => true

      #------------------#

      # @!method project_header_files=(project_header_files)
      #
      #   A list of file patterns that should be used to mark project headers.
      #
      #   ---
      #
      #   These patterns are matched against the public headers (or all the
      #   headers if no public headers have been specified) to exclude those
      #   headers which should not be exposed to the user project and which
      #   should not be used to generate the documentation. When the library
      #   is built, these headers will _not_ appear in the build directory.
      #
      #
      #   @example
      #
      #     spec.project_header_files = 'Headers/Project/*.h'
      #
      #   @param  [String, Array<String>] project_header_files
      #           the project headers of the Pod.
      #
      attribute :project_header_files,
                :container => Array,
                :file_patterns => true

      #------------------#

      # @!method private_header_files=(private_header_files)
      #
      #   A list of file patterns that should be used to mark private headers.
      #
      #   ---
      #
      #   These patterns are matched against the public headers (or all the
      #   headers if no public headers have been specified) to exclude those
      #   headers which should not be exposed to the user project and which
      #   should not be used to generate the documentation. When the library
      #   is built, these headers will appear in the build directory.
      #
      #   Header files that are not listed as neither public nor project or private will
      #   be treated as private, but in addition will not appear in the build
      #   directory at all.
      #
      #
      #   @example
      #
      #     spec.private_header_files = 'Headers/Private/*.h'
      #
      #   @param  [String, Array<String>] private_header_files
      #           the private headers of the Pod.
      #
      attribute :private_header_files,
                :container => Array,
                :file_patterns => true

      #------------------#

      # @!method vendored_frameworks=(*frameworks)
      #
      #   The paths of the framework bundles that come shipped with the Pod. Supports both `.framework` and `.xcframework` bundles.
      #   The frameworks will be made available to the Pod and to the consumers of the pod.
      #
      #   @example
      #
      #     spec.ios.vendored_frameworks = 'Frameworks/MyFramework.framework'
      #
      #   @example
      #
      #     spec.vendored_frameworks = 'MyFramework.framework', 'TheirFramework.xcframework'
      #
      #   @param  [String, Array<String>] vendored_frameworks
      #           A list of framework bundles paths.
      #
      attribute :vendored_frameworks,
                :container => Array,
                :file_patterns => true,
                :singularize => true

      #------------------#

      # @!method vendored_libraries=(*frameworks)
      #
      #   The paths of the libraries that come shipped with the Pod. The libraries will be available to the Pod and the
      #   consumers of the Pod.
      #
      #   @example
      #
      #     spec.ios.vendored_library = 'Libraries/libProj4.a'
      #
      #   @example
      #
      #     spec.vendored_libraries = 'libProj4.a', 'libJavaScriptCore.a'
      #
      #   @param  [String, Array<String>] vendored_libraries
      #           A list of library paths.
      #
      attribute :vendored_libraries,
                :container => Array,
                :file_patterns => true,
                :singularize => true

      #------------------#

      # The keys accepted by the category attribute for each on demand resource entry.
      #
      ON_DEMAND_RESOURCES_CATEGORY_KEYS = [:download_on_demand, :prefetched, :initial_install].freeze

      # @!method on_demand_resources=(on_demand_resources)
      #
      #   A hash of on demand resources that should be copied into the target bundle. Resources specified here
      #   will automatically become part of the resources build phase of the target this pod is integrated into.
      #
      #   If no category is specified then `:download_on_demand` is used as the default.
      #
      #   @note
      #
      #   Tags specified by pods are _always_ managed by CocoaPods. If a tag is renamed, changed or deleted then
      #   CocoaPods will update the tag within the targets the pod was integrated into. It is highly recommended not to
      #   share the same tags for your project as the ones used by the pods your project consumes.
      #
      #   @example
      #
      #     s.on_demand_resources = {
      #       'Tag1' => 'file1.png'
      #     }
      #
      #   @example
      #
      #     s.on_demand_resources = {
      #       'Tag1' => ['file1.png', 'file2.png']
      #     }
      #
      #   @example
      #
      #     s.on_demand_resources = {
      #       'Tag1' => { :paths => ['file1.png', 'file2.png'], :category => :download_on_demand }
      #     }
      #
      #   @example
      #
      #     s.on_demand_resources = {
      #       'Tag1' => { :paths => ['file1.png', 'file2.png'], :category => :initial_install }
      #     }
      #
      #   @param  [Hash{String=>String}, Hash{String=>Array<String>}, Hash{String=>Hash}] on_demand_resources
      #           The on demand resources shipped with the Pod.
      #
      attribute :on_demand_resources,
                :types => [String, Array, Hash],
                :container => Hash,
                :file_patterns => true,
                :singularize => true

      #------------------#

      # @!method resource_bundles=(*resource_bundles)
      #
      #   This attribute allows to define the name and the file of the resource
      #   bundles which should be built for the Pod. They are specified as a
      #   hash where the keys represent the name of the bundles and the values
      #   the file patterns that they should include.
      #
      #   For building the Pod as a static library, we strongly **recommend**
      #   library developers to adopt resource bundles as there can be name
      #   collisions using the resources attribute.
      #
      #   The names of the bundles should at least include the name of the Pod
      #   to minimise the chance of name collisions.
      #
      #   To provide different resources per platform namespaced bundles *must*
      #   be used.
      #
      #   @example
      #
      #     spec.ios.resource_bundle = { 'MapBox' => 'MapView/Map/Resources/*.png' }
      #
      #   @example
      #
      #     spec.resource_bundles = {
      #         'MapBox' => ['MapView/Map/Resources/*.png'],
      #         'MapBoxOtherResources' => ['MapView/Map/OtherResources/*.png']
      #       }
      #
      #   @param  [Hash{String=>String}, Hash{String=>Array<String>}] resource_bundles
      #           A hash where the keys are the names of the resource bundles
      #           and the values are their relative file patterns.
      #
      attribute :resource_bundles,
                :types => [String, Array],
                :container => Hash,
                :file_patterns => true,
                :singularize => true

      #------------------#

      # @!method resources=(resources)
      #
      #   A list of resources that should be copied into the target bundle.
      #
      #   For building the Pod as a static library, we strongly **recommend**
      #   library developers to adopt [resource bundles](http://guides.cocoapods.org/syntax/podspec.html#resource_bundles)
      #   as there can be name collisions using the resources attribute.
      #   Moreover, resources specified with this attribute are copied
      #   directly to the client target and therefore they are not
      #   optimised by Xcode.
      #
      #   @example
      #
      #     spec.resource = 'Resources/HockeySDK.bundle'
      #
      #   @example
      #
      #     spec.resources = ['Images/*.png', 'Sounds/*']
      #
      #   @param  [String, Array<String>] resources
      #           The resources shipped with the Pod.
      #
      attribute :resources,
                :container     => Array,
                :file_patterns => true,
                :singularize   => true

      #------------------#

      # @!method exclude_files=(exclude_files)
      #
      #   A list of file patterns that should be excluded from the other
      #   file patterns.
      #
      #   @example
      #
      #     spec.ios.exclude_files = 'Classes/osx'
      #
      #   @example
      #
      #     spec.exclude_files = 'Classes/**/unused.{h,m}'
      #
      #   @param  [String, Array<String>] exclude_files
      #           the file patterns that the Pod should ignore.
      #
      attribute :exclude_files,
                :container     => Array,
                :file_patterns => true

      #------------------#

      # @!method preserve_paths=(preserve_paths)
      #
      #   Any file that should **not** be removed after being downloaded.
      #
      #   ---
      #
      #   By default, CocoaPods removes all files that are not matched by any
      #   of the other file pattern.
      #
      #   @example
      #
      #     spec.preserve_path = 'IMPORTANT.txt'
      #
      #   @example
      #
      #     spec.preserve_paths = 'Frameworks/*.framework'
      #
      #   @param  [String, Array<String>] preserve_paths
      #           the paths that should be not cleaned.
      #
      attribute :preserve_paths,
                :container     => Array,
                :file_patterns => true,
                :singularize   => true

      #------------------#

      # @!method module_map=(module_map)
      #
      #   The module map file that should be used when this pod is integrated as
      #   a framework.
      #
      #   `false` indicates that the default CocoaPods `modulemap` file should not
      #   be generated.
      #
      #   `true` is the default and indicates that the default CocoaPods
      #   `modulemap` file should be generated.
      #
      #   ---
      #
      #   By default, CocoaPods creates a module map file based upon the public
      #   headers in a specification.
      #
      #   @example
      #
      #     spec.module_map = 'source/module.modulemap'
      #
      #   @example
      #
      #     spec.module_map = false
      #
      #   @param  [String, Bool] module_map
      #           the path to the module map file that should be used
      #           or whether to disable module_map generation.
      #
      attribute :module_map,
                :types => [TrueClass, FalseClass, String],
                :root_only => true

      #-----------------------------------------------------------------------#

      # @!group Subspecs
      #
      #   A library can specify a dependency on either another library, a
      #   subspec of another library, or a subspec of itself.

      #-----------------------------------------------------------------------#

      # Represents specification for a module of the library.
      #
      # ---
      #
      # Subspecs participate on a dual hierarchy.
      #
      # On one side, a specification automatically inherits as a dependency all
      # it children ‘sub-specifications’ (unless a default subspec is
      # specified).
      #
      # On the other side, a ‘sub-specification’ inherits the value of the
      # attributes of the parents so common values for attributes can be
      # specified in the ancestors.
      #
      # Although it sounds complicated in practice it means that subspecs in
      # general do what you would expect:
      #
      #     pod 'ShareKit', '2.0'
      #
      # Installs ShareKit with all the sharers like `ShareKit/Evernote`,
      # `ShareKit/Facebook`, etc, as they are defined as subspecs.
      #
      #     pod 'ShareKit/Twitter',  '2.0'
      #     pod 'ShareKit/Pinboard', '2.0'
      #
      # Installs ShareKit with only the source files for `ShareKit/Twitter`,
      # `ShareKit/Pinboard`. Note that, in this case, the ‘sub-specifications’
      # to compile need the source files, the dependencies, and the other
      # attributes defined by the root specification. CocoaPods is smart enough
      # to handle any issues arising from duplicate attributes.
      #
      # @example Subspecs with different source files.
      #
      #   subspec 'Twitter' do |sp|
      #     sp.source_files = 'Classes/Twitter'
      #   end
      #
      #   subspec 'Pinboard' do |sp|
      #     sp.source_files = 'Classes/Pinboard'
      #   end
      #
      # @example Subspecs referencing dependencies to other subspecs.
      #
      #   Pod::Spec.new do |s|
      #     s.name = 'RestKit'
      #
      #     s.subspec 'Core' do |cs|
      #       cs.dependency 'RestKit/ObjectMapping'
      #       cs.dependency 'RestKit/Network'
      #       cs.dependency 'RestKit/CoreData'
      #     end
      #
      #     s.subspec 'ObjectMapping' do |os|
      #     end
      #   end
      #
      # @example Nested subspecs.
      #
      #   Pod::Spec.new do |s|
      #     s.name = 'Root'
      #
      #     s.subspec 'Level_1' do |sp|
      #       sp.subspec 'Level_2' do |ssp|
      #       end
      #     end
      #   end
      #
      def subspec(name, &block)
        subspec = Specification.new(self, name, &block)
        @subspecs << subspec
        subspec
      end

      # The list of the test types currently supported.
      #
      SUPPORTED_TEST_TYPES = [:unit, :ui].freeze

      # The test type this specification supports. This only applies to test specifications.
      #
      # ---
      #
      # @example
      #
      #   test_spec.test_type = :unit
      #
      # @example
      #
      #   test_spec.test_type = 'unit'
      #
      # @param  [Symbol, String] type
      #         The test type to use.
      #
      attribute :test_type,
                :types => [Symbol, String],
                :multi_platform => false,
                :spec_types => [:test]

      # @!method requires_app_host=(flag)
      #
      #   Whether a test specification requires an app host to run tests. This only applies to test specifications.
      #
      #   @example
      #
      #     test_spec.requires_app_host = true
      #
      #   @param [Boolean] flag
      #          whether a test specification requires an app host to run tests.
      #
      attribute :requires_app_host,
                :types => [TrueClass, FalseClass],
                :default_value => false,
                :spec_types => [:test]

      # @!method app_host_name=(name)
      #
      #   The app specification to use as an app host, if necessary.
      #
      # @note
      #
      #    You must depend on that app spec using `test_spec.dependency 'PodName'`.
      #
      # @example
      #
      #   Pod::Spec.new do |spec|
      #     spec.name = 'NSAttributedString+CCLFormat'
      #
      #     spec.test_spec do |test_spec|
      #       test_spec.source_files = 'NSAttributedString+CCLFormatTests.m'
      #       test_spec.requires_app_host = true
      #       test_spec.app_host_name = 'NSAttributedString+CCLFormat/App'
      #       test_spec.dependency 'NSAttributedString+CCLFormat/App'
      #     end
      #
      #     spec.app_spec 'App' do |app_spec|
      #       app_spec.source_files = 'NSAttributedString+CCLFormat.m'
      #       app_spec.dependency 'AFNetworking'
      #     end
      #   end
      #
      #   @param [String] name
      #          The app specification to use as an app host, if necessary.
      #
      attribute :app_host_name,
                :types => [String],
                :spec_types => [:test]

      SCHEME_KEYS = [:launch_arguments, :environment_variables, :code_coverage, :parallelizable, :build_configurations].freeze

      # @!method scheme=(flag)
      #
      #   Specifies the scheme configuration to be used for this specification.
      #
      #   ---
      #
      #   @example
      #
      #     spec.scheme = { :launch_arguments => ['Arg1'] }
      #
      #   @example
      #
      #     spec.scheme = { :launch_arguments => ['Arg1', 'Arg2'], :environment_variables => { 'Key1' => 'Val1'} }
      #
      #   @param  [Hash] scheme
      #           the scheme configuration to be used for this specification.
      #
      attribute :scheme,
                :types => [Hash],
                :container => Hash,
                :keys => SCHEME_KEYS

      # Represents a test specification for the library. Here you can place all
      # your tests for your podspec along with the test dependencies.
      #
      # ---
      #
      # @example
      #
      #   Pod::Spec.new do |spec|
      #     spec.name = 'NSAttributedString+CCLFormat'
      #
      #     spec.test_spec do |test_spec|
      #       test_spec.source_files = 'NSAttributedString+CCLFormatTests.m'
      #       test_spec.dependency 'Expecta'
      #     end
      #   end
      #
      def test_spec(name = 'Tests', &block)
        subspec = Specification.new(self, name, true, &block)
        @subspecs << subspec
        subspec
      end

      # Represents an app specification for the library. Here you can place all
      # your app source files for your podspec along with the app dependencies.
      #
      # ---
      #
      # @example
      #
      #   Pod::Spec.new do |spec|
      #     spec.name = 'NSAttributedString+CCLFormat'
      #
      #     spec.app_spec do |app_spec|
      #       app_spec.source_files = 'NSAttributedString+CCLFormat.m'
      #       app_spec.dependency 'AFNetworking'
      #     end
      #   end
      #
      def app_spec(name = 'App', &block)
        appspec = Specification.new(self, name, :app_specification => true, &block)
        @subspecs << appspec
        appspec
      end

      #------------------#

      # @!method default_subspecs=(subspec_array)
      #
      #   An array of subspecs names that should be used as preferred dependency.
      #   If not specified, a specification requires all of its subspecs as
      #   dependencies.
      #
      #   You may use the value `:none` to specify that none of the subspecs are
      #   required to compile this pod and that all subspecs are optional.
      #
      #   ---
      #
      #   A Pod should make available the full library by default. Users can
      #   fine tune their dependencies, and exclude unneeded subspecs, once
      #   their requirements are known. Therefore, this attribute is rarely
      #   needed. It is intended to be used to select a default if there are
      #   ‘sub-specifications’ which provide alternative incompatible
      #   implementations, or to exclude modules rarely needed (especially if
      #   they trigger dependencies on other libraries).
      #
      #   @example
      #
      #     spec.default_subspec = 'Core'
      #
      #   @example
      #
      #     spec.default_subspecs = 'Core', 'UI'
      #
      #   @example
      #
      #     spec.default_subspecs = :none
      #
      #   @param  [Array<String>, String, Symbol] subspec_names
      #           An array of subspec names that should be inherited as
      #           dependency.
      #
      root_attribute :default_subspecs,
                     :container => Array,
                     :types => [Array, String, Symbol],
                     :singularize => true

      #-----------------------------------------------------------------------#

      # @!group Multi-Platform support
      #
      #   A specification can store values which are specific to only one
      #   platform.
      #
      #   ---
      #
      #   For example one might want to store resources which are specific to
      #   only iOS projects.
      #
      #       spec.resources = 'Resources/**/*.png'
      #       spec.ios.resources = 'Resources_ios/**/*.png'

      #-----------------------------------------------------------------------#

      # Provides support for specifying iOS attributes.
      #
      # @example
      #   spec.ios.source_files = 'Classes/ios/**/*.{h,m}'
      #
      # @return [PlatformProxy] the proxy that will set the attributes.
      #
      def ios
        PlatformProxy.new(self, :ios)
      end

      # Provides support for specifying OS X attributes.
      #
      # @example
      #   spec.osx.source_files = 'Classes/osx/**/*.{h,m}'
      #
      # @return [PlatformProxy] the proxy that will set the attributes.
      #
      def osx
        PlatformProxy.new(self, :osx)
      end

      alias macos osx

      # Provides support for specifying tvOS attributes.
      #
      # @example
      #   spec.tvos.source_files = 'Classes/tvos/**/*.{h,m}'
      #
      # @return [PlatformProxy] the proxy that will set the attributes.
      #
      def tvos
        PlatformProxy.new(self, :tvos)
      end

      # Provides support for specifying visionOS attributes.
      #
      # @example
      #   spec.visionos.source_files = 'Classes/visionos/**/*.{h,m}'
      #
      # @return [PlatformProxy] the proxy that will set the attributes.
      #
      def visionos
        PlatformProxy.new(self, :visionos)
      end

      # Provides support for specifying watchOS attributes.
      #
      # @example
      #   spec.watchos.source_files = 'Classes/watchos/**/*.{h,m}'
      #
      # @return [PlatformProxy] the proxy that will set the attributes.
      #
      def watchos
        PlatformProxy.new(self, :watchos)
      end
    end
  end
end