-
Notifications
You must be signed in to change notification settings - Fork 464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generate targets with same build settings as Xcode (related to #164) #166
Changes from 54 commits
8ef9c82
46b90d1
a51dffc
833e33f
31c730e
572ce95
c9ecd0b
afd2edb
4194361
33f003d
e1c020d
83a3f6c
5ae36de
886844d
db515d5
3ab2285
4058031
a023ffe
7db99af
9f29175
798444c
371584f
d9477e2
f99758e
57ea9a9
376944e
eec0a06
cb3b71f
721c639
7be5b84
793de4d
2929408
aad5627
a5f5e89
0cdcc85
bc37041
e6b44ce
8f99508
35b33f4
2edb331
fabeebc
0c60019
3975679
54138e1
6de1027
c7b94a6
3e89602
7878d39
529f3b4
d2533bf
c17e795
f881e6b
25f291d
5215839
b6fbbf1
bc0b2d9
7163b7a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,6 +50,107 @@ begin | |
@rvm_ruby_dir ||= File.expand_path('../..', `which ruby`.strip) | ||
end | ||
|
||
# Common Build settings | ||
#-----------------------------------------------------------------------------# | ||
|
||
namespace :common_build_settings do | ||
PROJECT_PATH = 'Project/Project.xcodeproj' | ||
|
||
task :prepare do | ||
verbose false | ||
cd 'spec/fixtures/CommonBuildSettings' | ||
end | ||
|
||
desc "Create a new empty project" | ||
task :new_project => [:prepare] do | ||
verbose false | ||
require 'xcodeproj' | ||
title "Setup Boilerplate" | ||
|
||
confirm "Delete existing fixture project and all data" | ||
rm_rf 'Project/*' | ||
|
||
subtitle "Create a new fixture project" | ||
Xcodeproj::Project.new(PROJECT_PATH).save | ||
|
||
subtitle "Open the project …" | ||
sh 'open "Project/Project.xcodeproj"' | ||
end | ||
|
||
desc "Interactive walkthrough for creating fixture targets" | ||
task :targets => [:prepare] do | ||
verbose false | ||
require 'xcodeproj' | ||
|
||
title "Create Targets" | ||
subtitle "You will be guided how to *manually* create the needed targets." | ||
subtitle "Each target name will been copied to your clipboard." | ||
confirm "Make sure you have nothing unsaved there" | ||
|
||
targets = { | ||
"Objc_iOS_Native" => { platform: :ios, type: :application, language: :objc, how: "iOS > Master-Detail Application > Language: Objective-C" }, | ||
"Swift_iOS_Native" => { platform: :ios, type: :application, language: :swift, how: "iOS > Master-Detail Application > Language: Swift" }, | ||
"Objc_iOS_Framework" => { platform: :ios, type: :framework, language: :objc, how: "iOS > Cocoa Touch Framework > Language: Objective-C" }, | ||
"Swift_iOS_Framework" => { platform: :ios, type: :framework, language: :swift, how: "iOS > Cocoa Touch Framework > Language: Swift" }, | ||
"Objc_iOS_StaticLibrary" => { platform: :ios, type: :static_library, language: :objc, how: "iOS > Cocoa Touch Static Library" }, | ||
"Objc_OSX_Native" => { platform: :osx, type: :application, language: :objc, how: "OSX > Cocoa Application > Language: Objective-C" }, | ||
"Swift_OSX_Native" => { platform: :osx, type: :application, language: :swift, how: "OSX > Cocoa Application > Language: Swift" }, | ||
"Objc_OSX_Framework" => { platform: :osx, type: :framework, language: :objc, how: "OSX > Cocoa Framework > Language: Objective-C" }, | ||
"Swift_OSX_Framework" => { platform: :osx, type: :framework, language: :swift, how: "OSX > Cocoa Framework > Language: Swift" }, | ||
"Objc_OSX_StaticLibrary" => { platform: :osx, type: :static_library, language: :objc, how: "OSX > Library > Type: Static" }, | ||
"Objc_OSX_DynamicLibrary" => { platform: :osx, type: :dynamic_library, language: :objc, how: "OSX > Library > Type: Dynamic" }, | ||
"OSX_Bundle" => { platform: :osx, type: :bundle, how: "OSX > Bundle" }, | ||
} | ||
|
||
targets.each do |name, attributes| | ||
begin | ||
sh "printf '#{name}' | pbcopy" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NSA IS WATCHING! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see above: + subtitle "Each target name will been copied to your clipboard."
+ confirm "Make sure you have nothing unsaved there" Because this whole process is not completely automated and requires user interaction. |
||
confirm "Create a target named '#{name}' by: #{attributes[:how]}", false | ||
|
||
project = Xcodeproj::Project.open(PROJECT_PATH) | ||
raise "Project couldn't be opened." if project.nil? | ||
|
||
target = project.targets.find { |t| t.name == name } | ||
raise "Target wasn't found." if target.nil? | ||
|
||
raise "Platform doesn't match." unless target.platform_name == attributes[:platform] | ||
raise "Type doesn't match." unless target.symbol_type == attributes[:type] | ||
|
||
debug_config = target.build_configurations.find { |c| c.name == 'Debug' } | ||
raise "Debug configuration is missing" if debug_config.nil? | ||
|
||
release_config = target.build_configurations.find { |c| c.name == 'Release' } | ||
raise "Release configuration is missing" if release_config.nil? | ||
|
||
is_swift_present = debug_config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] != nil | ||
is_swift_expected = attributes[:language] == :swift | ||
raise "Language doesn't match." unless is_swift_present == is_swift_expected | ||
|
||
puts green("Target matches.") | ||
puts | ||
rescue StandardError => e | ||
puts "#{red(e.message)} Try again." | ||
retry | ||
end | ||
end | ||
|
||
puts green("All targets were been successfully created.") | ||
end | ||
|
||
desc "Dump the build settings of the fixture project to xcconfig files" | ||
task :dump => [:prepare] do | ||
verbose false | ||
sh "../../../bin/xcodeproj config-dump Project/Project.xcodeproj configs" | ||
end | ||
|
||
desc "Recreate the xcconfig files for the fixture project targets from scratch" | ||
task :rebuild => [ | ||
:new_project, | ||
:targets, | ||
:dump, | ||
] | ||
end | ||
|
||
#-----------------------------------------------------------------------------# | ||
|
||
namespace :spec do | ||
|
@@ -118,8 +219,31 @@ def yellow(string) | |
"\033[0;33m#{string}\e[0m" | ||
end | ||
|
||
# Colorizes a string to red. | ||
# | ||
def red(string) | ||
"\033[0;31m#{string}\e[0m" | ||
end | ||
|
||
# Colorizes a string to green. | ||
# | ||
def green(string) | ||
"\033[0;32m#{string}\e[0m" | ||
end | ||
|
||
# Colorizes a string to cyan. | ||
# | ||
def cyan(string) | ||
"\n\033[0;36m#{string}\033[0m" | ||
end | ||
|
||
def confirm(message, decline_by_default=true) | ||
options = ['y', 'n'] | ||
options[decline_by_default ? 1 : 0].upcase! | ||
print yellow("#{message}: [#{options.join('/')}] ") | ||
input = STDIN.gets.chomp | ||
if input == options[1].downcase || (input == '' && decline_by_default) | ||
puts red("Aborted by user.") | ||
exit 1 | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
module Xcodeproj | ||
class Command | ||
class ConfigDump < Command | ||
def self.banner | ||
<<-eos.strip_heredoc | ||
Dumps the build settings of all project targets for all configurations in | ||
directories named by the target in given output directory. | ||
|
||
$ config-dump PROJECT OUTPUT | ||
|
||
It extracts common build settings in a per target base.xcconfig file. | ||
If no `PROJECT` is specified then the current work directory is searched | ||
for one. | ||
If no `OUTPUT` is specified then the project directory will be used.%) | ||
eos | ||
end | ||
|
||
def initialize(argv) | ||
xcodeproj_path = argv.shift_argument | ||
@xcodeproj_path = File.expand_path(xcodeproj_path) if xcodeproj_path | ||
|
||
@output_path = Pathname(argv.shift_argument || '.') | ||
unless @output_path.directory? | ||
raise Informative, 'The output path must be a directory.' | ||
end | ||
|
||
super unless argv.empty? | ||
end | ||
|
||
def run | ||
dump_all_configs(xcodeproj, 'Project') | ||
|
||
xcodeproj.targets.each do |target| | ||
dump_all_configs(target, target.name) | ||
end | ||
end | ||
|
||
def dump_all_configs(configurable, name) | ||
path = Pathname(name) | ||
|
||
# Dump base configuration to file | ||
base_settings = extract_common_settings!(configurable.build_configurations) | ||
base_file_path = path + "#{name}_base.xcconfig" | ||
dump_config_to_file(base_settings, base_file_path) | ||
|
||
# Dump each configuration to file | ||
configurable.build_configurations.each do |config| | ||
settings = config.build_settings | ||
dump_config_to_file(settings, path + "#{name}_#{config.name.downcase}.xcconfig", [base_file_path]) | ||
end | ||
end | ||
|
||
def extract_common_settings!(build_configurations) | ||
# Grasp all common build settings | ||
all_build_settings = build_configurations.map(&:build_settings) | ||
common_build_settings = all_build_settings.reduce do |settings, config_build_settings| | ||
settings.select { |key, value| value == config_build_settings[key] } | ||
end | ||
|
||
# Remove all common build settings from each configuration specific build settings | ||
build_configurations.each do |config| | ||
config.build_settings.reject! { |key| !common_build_settings[key].nil? } | ||
end | ||
|
||
common_build_settings | ||
end | ||
|
||
def dump_config_to_file(settings, file_path, includes = []) | ||
dir = @output_path + file_path + '..' | ||
dir.mkdir unless dir.exist? | ||
|
||
config = Config.new(settings) | ||
config.includes = includes | ||
config.save_as(@output_path + file_path) | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -294,8 +294,9 @@ def extract_include(line) | |
# entry is the value. | ||
# | ||
def extract_key_value(line) | ||
key, value = line.split('=', 2) | ||
if key && value | ||
match = line.match(/([^=]*?(?:\[[^\]]*=?[^\]]*\])*)\s+=(.*)/) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is crazy There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I’d like this to have inline comments. (See http://www.ruby-doc.org/core-1.9.3/Regexp.html#class-Regexp-label-Free-Spacing+Mode+and+Comments) |
||
if match | ||
key, value = match[1], match[2] | ||
[key.strip, value.strip] | ||
else | ||
[] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably use
Bundler.require
instead. Otherwise if theRakefile
isn't ran with bundler we will potentially be running the wrong Xcodeproj.Most of the other tasks in our Rakefile's protect against this, such as running bacon via
sh "bundle exec bacon #{args.spec_file}"
.