class Fastlane::Actions::ReadXcconfigAction

Action to read and resolve values in Xcconfig files.

Public Class Methods

authors() click to toggle source

Plugin action authors.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 160
def self.authors
  ["Maksym Grebenets"]
end
available_options() click to toggle source

Plugin action available options.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 180
def self.available_options
  [
    FastlaneCore::ConfigItem.new(key: :path,
                            env_name: "XCCONFIG_ACTIONS_READ_PATH",
                         description: "Path to xcconfig to read",
                            optional: false,
                                type: String,
                        verify_block: proc do |value|
                                        UI.user_error!("Couldn't find xcconfig at path: '#{value}'") unless File.exist?(value)
                                      end),
    FastlaneCore::ConfigItem.new(key: :parent,
                            env_name: "XCCONFIG_ACTIONS_READ_PARENT",
                         description: "Parent xcconfig file to inherit build settings from.\nThis is the xcconfig you'd set on the project level in Xcode",
                            optional: true,
                                type: String,
                        verify_block: proc do |value|
                                        UI.user_error!("Couldn't find parent xcconfig at path: '#{value}'") if value && !File.exist?(value)
                                      end),
    FastlaneCore::ConfigItem.new(key: :resolve,
                            env_name: "XCCONFIG_ACTIONS_READ_RESOLVE",
                         description: "Resolve variables in xcconfigs",
                       default_value: true,
                                type: Boolean),
    FastlaneCore::ConfigItem.new(key: :srcroot,
                            env_name: "XCCONFIG_ACTIONS_READ_SRCROOT",
                         description: "Value for SRCROOT build setting, default is current working directory",
                            optional: true,
                                type: String),
    FastlaneCore::ConfigItem.new(key: :target_name,
                            env_name: "XCCONFIG_ACTIONS_READ_TARGET_NAME",
                         description: "Value for TARGET_NAME build setting",
                            optional: true,
                                type: String),
    FastlaneCore::ConfigItem.new(key: :output_path,
                            env_name: "XCCONFIG_ACTIONS_READ_OUTPUT_PATH",
                         description: "Output path",
                            optional: true,
                                type: String)
  ]
end
category() click to toggle source

Plugin action category.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 175
def self.category
  :building
end
description() click to toggle source

Plugin action description.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 155
def self.description
  "Read and resolve contents of xcconfig file and return as JSON"
end
details() click to toggle source

Plugin action details.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 170
def self.details
  ""
end
is_supported?(platform) click to toggle source

Check if platform is supported by the action. @param [Symbol] platform Platform to check. @return [Boolean] A Boolean indicating whether the platform is supported by the action.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 224
def self.is_supported?(platform)
  [:ios, :mac].include?(platform)
end
read_config(filename) click to toggle source

Read xcconfig value as a hash.

@param [String] filename Xcconfig path. @return [Hash<String,String>] Dictionary of xcconfig values.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 71
def self.read_config(filename)
  # TODO: If filename starts with <DEVELOPER_DIR>, then need to resolve it first.
  return {} if filename.nil? || !File.exist?(filename)

  # Used to use Xcodeproj::Config.new(filename) here, but it just doesn't do the job,
  # e.g. it resolves $(inherited) incorrectly, allowing it to work within the scope of one file
  # without any parent config.

  xcconfig = Helper::XcconfigActionsHelper.read_xcconfig(filename)
  config = xcconfig[:config]
  includes = xcconfig[:includes]

  # Xcodeproj does not resolve overrides from included files, so do it manually.
  resolved_includes_config = includes.reduce({}) do |resolved_config, include_path|
    resolved_path = resolve_path(include_path, relative_to: filename)
    resolved_config.merge(read_config(resolved_path))
  end

  config.merge(resolved_includes_config)
end
resolve(config, parent, srcroot, target_name) click to toggle source

Resolve config using parent information. @param [Hash] config Config to resolve. @param [Hash] parent Parent config. @param [String] srcroot Path to use for $(SRCROOT). @param [String] target_name Name to use for $(TARGET_NAME) @return [Hash] Resolved config.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 48
def self.resolve(config, parent, srcroot, target_name)
  parent_config = read_config(parent)

  parent_config["SRCROOT"] = srcroot
  parent_config["TARGET_NAME"] = target_name if target_name

  if Helper::XcconfigActionsHelper.command_exist?("xcodebuild")
    # Set value of XCODE_VERSION_MAJOR not available when reading xcconfigs directly.
    xcode_version = `xcodebuild -version | head -n1 | cut -d' ' -f2 | xargs`.strip
    xcode_version_major_padded = xcode_version.split(".").first.rjust(2, "0") + "00"
    parent_config["XCODE_VERSION_MAJOR"] = xcode_version_major_padded
  end

  resolved_parent_config = resolve_config(parent_config)
  resolved_config = resolve_config(config, parent: resolved_parent_config)

  resolved_parent_config.merge(resolved_config)
end
resolve_config(config, parent: {}) click to toggle source

Resolve xcconfig values using parent config.

@param [Hash<String,String>] config Current dictionary of values. @param [Hash<String,String>] parent Resolved parent xcconfig values.

@return [Hash<String,String>] Resolved xcconfig values.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 143
def self.resolve_config(config, parent: {})
  config.each do |k, v|
    resolve_value(v, key: k, resolved: config, parent: parent)
  end
  config
end
resolve_path(path, relative_to:) click to toggle source

Expand given path in relation to another path.

Used to resolved '#include “relative/path.xcconfig”' includes.

@param [String] path Path to expand. @param [String] relative_to Parent path to expand in relation to.

@return [String] Expanded path.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 100
def self.resolve_path(path, relative_to:)
  # Absolute or special SDK paths need no resolving.
  return path if path.start_with?("/", "<")

  File.expand_path(File.join(File.dirname(relative_to), path))
end
resolve_value(value, key:, resolved: {}, parent: {}) click to toggle source

Resolve xcconfig value, i.e. expand any of the $() variable references.

@param [String] value String value to resolve. @param [String] key Key under which this value is defined in xcconfig. This key will be used to pick up values from parent xcconfig. @param [Hash<String,String>] resolved Dictionary of already resolved values. @param [Hash<String,String>] parent Dictionary of parent xcconfig values.

@return [String] Resolved value.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 115
def self.resolve_value(value, key:, resolved: {}, parent: {})
  matches = value.scan(/(\$\([^$\)]*\))/)

  mutable_value = value.dup # Prevent unwanted side-effect of input modification.
  matches.each do |group|
    group.each do |match|
      var_name = match.delete("$()")
      # If inherited, use value from parent config.
      var_value = if var_name == "inherited"
                    parent[key]
                  else
                    resolved[var_name] || parent[var_name]
                  end
      mutable_value.gsub!(match, var_value || "")
      resolved[key] = mutable_value
    end
  end

  # If there are still variables, keep resolving then.
  mutable_value.include?("$(") ? resolve_value(mutable_value, key: key, resolved: resolved, parent: parent) : mutable_value
end
return_value() click to toggle source

Plugin action return value.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 165
def self.return_value
  "Parse and resolved build settings from xcconfig represented as JSON"
end
run(params) click to toggle source

Run action. @param [Hash] params Action parameters. @return [Hash] Xcconfig dictionary.

# File lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb, line 19
def self.run(params)
  path = params[:path]
  parent = params[:parent]
  srcroot = params[:srcroot] || Dir.pwd
  target_name = params[:target_name]

  config = read_config(path)

  config = resolve(config, parent, srcroot, target_name) if params[:resolve]

  Actions.lane_context[SharedValues::XCCONFIG_ACTIONS_BUILD_SETTINGS] = config

  if params[:output_path]
    File.open(params[:output_path], "w") { |f| f.puts(config.to_json) }
  else
    return config
  end
end