Skip to content

Instantly share code, notes, and snippets.

@alloy
Created April 28, 2020 14:43
Show Gist options
  • Save alloy/5d202d4a3fdb3844d0bc7490f628d671 to your computer and use it in GitHub Desktop.
Save alloy/5d202d4a3fdb3844d0bc7490f628d671 to your computer and use it in GitHub Desktop.
Example of ways to introspect Xcode build settings
# gem install xcodeproj claide hashdiff
require 'xcodeproj'
require 'claide'
require 'pp'
argv = CLAide::ARGV.new(ARGV)
project_path = argv.shift_argument
target_name = argv.option("target")
target_type = argv.option("target-type", "PBXNativeTarget")
config_name = argv.option("config")
diff = argv.flag?("diff")
intersection = argv.flag?("intersection")
if project_path.nil? || argv.flag?('help')
puts "Usage:"
puts "\truby #{__FILE__} path/to/the.xcodeproj [OPTIONS]"
puts
puts "Options:"
puts "\t--target-type=type\tThe types of targets to operate on, defaults to PBXNativeTarget"
puts "\t--target=name\t\tThe target to limit to"
puts "\t--config=name\t\tThe build configuration to operate on/limit to"
puts "\t--intersection\t\tFinds the intersection of all configurations"
puts "\t--diff\t\t\tWhen combined with `--intersection` target configurations,\n\t\t\t\tare diffed against the intersection; otherwise\n\t\t\t\tconfigurations in `target` are diffed against `config`"
puts
exit 1
end
def print_header(content)
puts "=" * 80
puts content
puts "=" * 80
end
def print_target_header(target, config)
print_header "#{target.name}: #{config.name}"
end
def dump_target(target, config_name = nil)
target.build_configurations.each do |config|
next if config_name && config.name != config_name
print_target_header(target, config)
pp config.build_settings
puts
end
end
def diff_target_config(target, base_config, skip_config_name = nil)
require 'hashdiff'
target.build_configurations.each do |config|
next if config.name == skip_config_name
print_target_header(target, config)
pp Hashdiff.diff(base_config, config.build_settings)
puts
end
end
def diff_target_config_to_others(target, config_name)
base_config = target.build_configurations.find { |c| c.name == config_name }
diff_target_config(target, base_config.build_settings, config_name)
end
def intersect(targets, config_name)
require 'hashdiff'
result = nil
targets.each do |target|
target.build_configurations.each do |config|
next if config_name && config.name != config_name
if result.nil?
result = config.build_settings
else
Hashdiff.diff(result, config.build_settings)
.reverse # Remove elements from back to front, otherwise indices will no longer be correct.
.each do |type, key, *val|
case type
when "-"
if key.include?("[")
_, key, index = key.match(/^(\w+)\[(\d+)\]$/).to_a
result[key].delete_at(index.to_i)
else
result.delete(key)
end
when "~"
before, after = Array(val[0]), Array(val[1])
before.delete_if { |x| !after.include?(x) }
case before.size
when 0
result.delete(key)
when 1
result[key] = before[0]
else
result[key] = before
end
else
end
end
end
end
end
result
end
project = Xcodeproj::Project.open(project_path)
targets = target_type ? project.targets.select { |t| t.class.name.split("::").last == target_type } : project.targets
if intersection
result = intersect(targets, config_name)
print_header("Intersection")
pp result
if diff
targets.each do |target|
next if target_name && target.name != target_name
diff_target_config(target, result)
end
end
else
if target_name
target = targets.find { |t| t.name == target_name }
if diff
diff_target_config_to_others(target, config_name)
else
dump_target(target, config_name)
end
else
targets.each do |target|
dump_target(target, config_name)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment