xcoder 0.1.15 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +6 -0
- data/.rbenv-version +1 -0
- data/.rvmrc +1 -1
- data/Gemfile +10 -2
- data/README.md +110 -9
- data/Rakefile +2 -2
- data/bin/xcoder +74 -0
- data/lib/xcode/builder.rb +1 -2
- data/lib/xcode/builder/base_builder.rb +231 -102
- data/lib/xcode/builder/build_parser.rb +146 -0
- data/lib/xcode/builder/project_target_config_builder.rb +2 -2
- data/lib/xcode/builder/scheme_builder.rb +29 -12
- data/lib/xcode/buildspec.rb +286 -0
- data/lib/xcode/configuration_list.rb +24 -24
- data/lib/xcode/deploy/ftp.rb +56 -0
- data/lib/xcode/deploy/kickfolio.rb +18 -0
- data/lib/xcode/deploy/s3.rb +38 -0
- data/lib/xcode/deploy/ssh.rb +43 -0
- data/lib/xcode/deploy/templates/index.rhtml +22 -0
- data/lib/xcode/deploy/templates/manifest.rhtml +31 -0
- data/lib/xcode/deploy/testflight.rb +32 -27
- data/lib/xcode/deploy/web_assets.rb +39 -0
- data/lib/xcode/info_plist.rb +16 -0
- data/lib/xcode/keychain.rb +33 -10
- data/lib/xcode/platform.rb +65 -0
- data/lib/xcode/project.rb +7 -3
- data/lib/xcode/provisioning_profile.rb +38 -2
- data/lib/xcode/scheme.rb +44 -17
- data/lib/xcode/shell/command.rb +79 -5
- data/lib/xcode/terminal_output.rb +116 -0
- data/lib/xcode/test/formatters/junit_formatter.rb +7 -2
- data/lib/xcode/test/formatters/stdout_formatter.rb +34 -25
- data/lib/xcode/test/parsers/kif_parser.rb +87 -0
- data/lib/xcode/test/parsers/ocunit_parser.rb +3 -3
- data/lib/xcode/version.rb +1 -1
- data/lib/xcode/workspace.rb +13 -5
- data/lib/xcoder.rb +33 -31
- data/spec/TestProject/TestProject.xcodeproj/project.pbxproj +1627 -1015
- data/spec/TestWorkspace.xcworkspace/contents.xcworkspacedata +7 -0
- data/spec/builder_spec.rb +87 -71
- data/spec/deploy_spec.rb +63 -0
- data/spec/ocunit_parser_spec.rb +1 -1
- data/xcoder.gemspec +3 -1
- metadata +95 -19
- data/lib/xcode/buildfile.rb +0 -101
- data/lib/xcode/shell.rb +0 -26
- data/spec/deploy_testflight_spec.rb +0 -27
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'xcode/test/report'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Xcode
|
5
|
+
module Builder
|
6
|
+
class XcodebuildParser
|
7
|
+
include Xcode::TerminalOutput
|
8
|
+
attr_accessor :suppress_warnings
|
9
|
+
|
10
|
+
KNOWN_STEPS = [
|
11
|
+
'Clean.Remove',
|
12
|
+
'Build',
|
13
|
+
'Check',
|
14
|
+
'ProcessInfoPlistFile',
|
15
|
+
'CpResource',
|
16
|
+
'ProcessPCH',
|
17
|
+
'CompileC',
|
18
|
+
'Ld',
|
19
|
+
'CreateUniversalBinary',
|
20
|
+
'GenerateDSYMFile',
|
21
|
+
'CopyPNGFile',
|
22
|
+
'CompileXIB',
|
23
|
+
'CopyStringsFile',
|
24
|
+
'ProcessProductPackaging',
|
25
|
+
'Touch',
|
26
|
+
'CodeSign',
|
27
|
+
'Libtool',
|
28
|
+
'PhaseScriptExecution',
|
29
|
+
'Validate'
|
30
|
+
# 'SymLink'
|
31
|
+
]
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
@last_good_index = 0
|
35
|
+
@last_step_name = nil
|
36
|
+
@last_step_params = []
|
37
|
+
@suppress_warnings = false
|
38
|
+
end
|
39
|
+
|
40
|
+
def close
|
41
|
+
end
|
42
|
+
|
43
|
+
def << piped_row
|
44
|
+
piped_row = piped_row.force_encoding("UTF-8").gsub(/\n$/,'')
|
45
|
+
|
46
|
+
if piped_row=~/^\s+/
|
47
|
+
@last_step_params << piped_row
|
48
|
+
else
|
49
|
+
if piped_row=~/\=\=\=\s/
|
50
|
+
# This is just an info
|
51
|
+
elsif piped_row=~/Build settings from command line/
|
52
|
+
# Ignore
|
53
|
+
elsif piped_row=~/Check dependencies/
|
54
|
+
# Ignore
|
55
|
+
elsif piped_row==''
|
56
|
+
# Empty line, ignore
|
57
|
+
elsif piped_row=~/[A-Z]+\s\=\s/
|
58
|
+
# some build env info
|
59
|
+
elsif piped_row=~/^warning:/i
|
60
|
+
log_xcode "#{piped_row.gsub(/^warning:\s/i,'')}", :warning
|
61
|
+
elsif piped_row=~/^\[BEROR\](.*)/
|
62
|
+
log_xcode "#{$1}", :error
|
63
|
+
elsif piped_row=~/^ld: warning:/i
|
64
|
+
log_xcode "#{piped_row.gsub(/^ld: warning:\s/i,'')}", :warning
|
65
|
+
elsif piped_row=~/Unable to validate your application/
|
66
|
+
log_xcode piped_row, :warning
|
67
|
+
# print "\n warning: ", :yellow
|
68
|
+
# print " #{piped_row}"
|
69
|
+
elsif piped_row=~/bin\/libtool\:\s(.*)/
|
70
|
+
log_xcode $1, :warning, :libtool
|
71
|
+
|
72
|
+
# Pick up success
|
73
|
+
elsif piped_row=~/\*\*\s.*SUCCEEDED\s\*\*/
|
74
|
+
# yay, all good
|
75
|
+
print "\n"
|
76
|
+
|
77
|
+
elsif piped_row=~/^cp\s\-R/
|
78
|
+
# Ignore
|
79
|
+
|
80
|
+
elsif piped_row=~/\sdeclared\shere$/
|
81
|
+
# Ignore
|
82
|
+
|
83
|
+
# Pick up warnings/notes/errors
|
84
|
+
elsif piped_row=~/^(.*:\d+:\d+): (\w+): (.*)$/
|
85
|
+
# This is a warning/note/error
|
86
|
+
type = $2.downcase
|
87
|
+
level = :info
|
88
|
+
if type=="warning"
|
89
|
+
level = :warning
|
90
|
+
elsif type=="error"
|
91
|
+
level = :error
|
92
|
+
end
|
93
|
+
|
94
|
+
if (level==:warning or level==:note) and @suppress_warnings
|
95
|
+
# ignore
|
96
|
+
else
|
97
|
+
log_xcode $3, level
|
98
|
+
log_xcode " at #{$1}", level
|
99
|
+
# print "\n#{level.rjust(8)}: ", color
|
100
|
+
# print $3
|
101
|
+
# print "\n at #{$1}"
|
102
|
+
end
|
103
|
+
|
104
|
+
# If there were warnings, this will be output
|
105
|
+
elsif piped_row=~/\d+\swarning(s?)\sgenerated\./
|
106
|
+
# TODO: is this safe to ignore?
|
107
|
+
|
108
|
+
|
109
|
+
# This might be a build step
|
110
|
+
else
|
111
|
+
step = piped_row.scan(/^(\S+)/).first.first
|
112
|
+
if KNOWN_STEPS.include? step
|
113
|
+
unless @last_step_name==step
|
114
|
+
# print "\n" unless @last_step_name.nil?
|
115
|
+
@last_step_name = step
|
116
|
+
@last_step_params = []
|
117
|
+
# print "#{"run".rjust(8)}: ", :green
|
118
|
+
# print "#{step} "
|
119
|
+
end
|
120
|
+
unless @need_cr
|
121
|
+
log_xcode step+" ", :info, false
|
122
|
+
end
|
123
|
+
print '.', :green
|
124
|
+
else
|
125
|
+
# Echo unknown output
|
126
|
+
unless @suppress_warnings
|
127
|
+
log_xcode piped_row, :info
|
128
|
+
# print "\n > ", :blue
|
129
|
+
# print "#{piped_row}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
rescue => e
|
135
|
+
puts "Failed to parse '#{piped_row}' because #{e}", :red
|
136
|
+
end # <<
|
137
|
+
|
138
|
+
def log_xcode message, level, cr=true, task = :xcode
|
139
|
+
print "\n" if @need_cr
|
140
|
+
print_task task, message, level, cr
|
141
|
+
@need_cr = !cr
|
142
|
+
end
|
143
|
+
|
144
|
+
end # XcodebuildParser
|
145
|
+
end # Builder
|
146
|
+
end # Xcode
|
@@ -2,8 +2,8 @@ module Xcode
|
|
2
2
|
module Builder
|
3
3
|
class ProjectTargetConfigBuilder < BaseBuilder
|
4
4
|
|
5
|
-
def
|
6
|
-
cmd = super
|
5
|
+
def prepare_xcodebuild sdk=nil
|
6
|
+
cmd = super sdk
|
7
7
|
cmd << "-project \"#{@target.project.path}\""
|
8
8
|
cmd << "-target \"#{@target.name}\""
|
9
9
|
cmd << "-config \"#{@config.name}\""
|
@@ -1,32 +1,49 @@
|
|
1
|
-
module Xcode
|
2
|
-
|
1
|
+
module Xcode
|
2
|
+
|
3
3
|
class Workspace
|
4
4
|
def to_xcodebuild_option
|
5
5
|
"-workspace \"#{self.path}\""
|
6
6
|
end
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
class Project
|
10
10
|
def to_xcodebuild_option
|
11
11
|
"-project \"#{self.path}\""
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
module Builder
|
16
16
|
class SchemeBuilder < BaseBuilder
|
17
|
-
|
17
|
+
|
18
18
|
def initialize(scheme)
|
19
|
-
@scheme = scheme
|
20
|
-
|
19
|
+
@scheme = scheme
|
20
|
+
@target = @scheme.build_targets.last
|
21
|
+
super @target, @target.config(@scheme.archive_config)
|
21
22
|
end
|
22
|
-
|
23
|
-
def
|
24
|
-
cmd = super
|
25
|
-
cmd << @scheme.parent.to_xcodebuild_option
|
23
|
+
|
24
|
+
def prepare_xcodebuild sdk=nil
|
25
|
+
cmd = super sdk
|
26
|
+
cmd << @scheme.parent.to_xcodebuild_option
|
26
27
|
cmd << "-scheme \"#{@scheme.name}\""
|
28
|
+
cmd << "-configuration \"#{@scheme.archive_config}\""
|
27
29
|
cmd
|
28
30
|
end
|
31
|
+
|
32
|
+
# def prepare_test_command sdk=@sdk
|
33
|
+
# cmd = super sdk
|
34
|
+
# cmd << "-scheme \"#{@scheme.name}\""
|
35
|
+
# cmd << "-configuration \"#{@scheme.test_config}\""
|
36
|
+
# cmd
|
37
|
+
# end
|
29
38
|
|
39
|
+
def test
|
40
|
+
unless @scheme.testable?
|
41
|
+
print_task :builder, "Nothing to test", :warning
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
30
47
|
end
|
31
48
|
end
|
32
|
-
end
|
49
|
+
end
|
@@ -0,0 +1,286 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
module Xcode
|
5
|
+
|
6
|
+
class Buildspec
|
7
|
+
|
8
|
+
#
|
9
|
+
# Given a path to a Buildspec, define rake tasks
|
10
|
+
#
|
11
|
+
# @param the path to the Buildspec file, or a directory containing one
|
12
|
+
#
|
13
|
+
def self.parse(path = '.')
|
14
|
+
b = GroupBuilder.new
|
15
|
+
path = File.join(path, 'Buildspec') if File.directory? path
|
16
|
+
b.instance_eval(File.read(path))
|
17
|
+
b.generate_rake_tasks
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Given a path to a buildspec, perform the given task
|
22
|
+
#
|
23
|
+
# @param the path to the buildspec, or the directory containing one
|
24
|
+
# @param the task to invoke
|
25
|
+
#
|
26
|
+
def self.run(path, task)
|
27
|
+
self.parse path
|
28
|
+
Rake::Task[task].invoke unless task.nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
class GroupBuilder < ::Rake::TaskLib
|
32
|
+
include ::Rake::DSL if defined?(::Rake::DSL)
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
@groups = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def group group, &block
|
39
|
+
@groups << group.downcase
|
40
|
+
namespace group.downcase do
|
41
|
+
t = TaskBuilder.new
|
42
|
+
t.instance_eval(&block)
|
43
|
+
t.generate_rake_tasks
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def generate_rake_tasks
|
48
|
+
|
49
|
+
# namespace :all do
|
50
|
+
# define top level tasks
|
51
|
+
desc "Build all"
|
52
|
+
task :build => @groups.map {|g| "#{g}:build"}
|
53
|
+
|
54
|
+
desc "Clean all"
|
55
|
+
task :clean => @groups.map {|g| "#{g}:clean"}
|
56
|
+
|
57
|
+
desc "Package all"
|
58
|
+
task :package => @groups.map {|g| "#{g}:package"}
|
59
|
+
|
60
|
+
desc "Deploy all"
|
61
|
+
task :deploy => @groups.map {|g| "#{g}:deploy:all"}
|
62
|
+
# end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class TaskBuilder < ::Rake::TaskLib
|
67
|
+
|
68
|
+
include ::Rake::DSL if defined?(::Rake::DSL)
|
69
|
+
|
70
|
+
def initialize
|
71
|
+
@before = lambda {|builder| return nil }
|
72
|
+
@deployments = []
|
73
|
+
@build_number = lambda do
|
74
|
+
timestamp = Time.now.strftime("%Y%m%d%H%M%S")
|
75
|
+
ENV['BUILD_NUMBER']||"SNAPSHOT-#{Socket.gethostname}-#{timestamp}"
|
76
|
+
end
|
77
|
+
|
78
|
+
# @profile = "Provisioning/#{name}.mobileprovision"
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# Use the given project/workspace file
|
83
|
+
#
|
84
|
+
# This must be provided.
|
85
|
+
#
|
86
|
+
# @param the path to the .xcodeproj or .xcworkspace
|
87
|
+
# @param a hash containing {:scheme => ''} or {:target => '', :config => ''}
|
88
|
+
#
|
89
|
+
def use filename, args={}
|
90
|
+
@filename = filename
|
91
|
+
@args = args
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# A block to run before each builder invocation
|
96
|
+
#
|
97
|
+
# If supplied, the block will be yielded the builder object just before the invocation
|
98
|
+
# of clean/build/package
|
99
|
+
#
|
100
|
+
# @param the block to call
|
101
|
+
#
|
102
|
+
def before &block
|
103
|
+
@before = block
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# A block to generate the build number
|
108
|
+
#
|
109
|
+
# This is optional, by default it will use ENV['BUILD_NUMBER'] or create a snapshot
|
110
|
+
# filename based on the hostname and time
|
111
|
+
#
|
112
|
+
# @param the block to generate the build number
|
113
|
+
#
|
114
|
+
def build_number &block
|
115
|
+
@build_number = block
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Configure a keychain to use (optional)
|
120
|
+
#
|
121
|
+
# If specified, the keychain at the given path will be unlocked during the build and
|
122
|
+
# the first identity will be set on the builder
|
123
|
+
#
|
124
|
+
# @param the path to the keychain
|
125
|
+
# @param the password to unlock the keychain
|
126
|
+
#
|
127
|
+
def keychain path, password = nil
|
128
|
+
@keychain = {:path => path, :password => password}
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
#
|
133
|
+
# Specify the platform to build for
|
134
|
+
#
|
135
|
+
# @param the platform, can be 'iphone', 'iphonesimulator', 'macosx'
|
136
|
+
# @param the version, can be any valid, installed sdk version for the appropriate platform or nil
|
137
|
+
def platform name, version=nil
|
138
|
+
@platform = Xcode::Platforms.find name, version
|
139
|
+
end
|
140
|
+
|
141
|
+
#
|
142
|
+
# Set's the identity to use to sign the package
|
143
|
+
#
|
144
|
+
# This should be the name of the identity in your keychain, such as
|
145
|
+
# 'iPhone Distribution: My Name'
|
146
|
+
#
|
147
|
+
# @param the name of the identity
|
148
|
+
#
|
149
|
+
def identity identity
|
150
|
+
@identity = identity
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
# Set the profile (i.e. .mobileprovision) to use
|
155
|
+
#
|
156
|
+
# @param the name or path to the profile
|
157
|
+
def profile profile
|
158
|
+
@profile = profile
|
159
|
+
end
|
160
|
+
|
161
|
+
#
|
162
|
+
# Set a deployment target.
|
163
|
+
#
|
164
|
+
# This will configure a set of deploy: targets that
|
165
|
+
# will send the .ipa to various services (testflight, s3, ftp, sftp, etc)
|
166
|
+
#
|
167
|
+
# @param the deployment type (testflight, etc)
|
168
|
+
# @param arguments to pass to the deployment type
|
169
|
+
#
|
170
|
+
def deploy type, args = {}
|
171
|
+
@deployments << {:type => type, :args => args}
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
def notify type, args = {}
|
176
|
+
@notifications << {:type => type, :args => args}
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
#
|
181
|
+
# Internally used to lazily instantiate the builder given the properties that
|
182
|
+
# have been set.
|
183
|
+
#
|
184
|
+
# @return the appropriate builder
|
185
|
+
#
|
186
|
+
def builder
|
187
|
+
return @builder unless @builder.nil?
|
188
|
+
|
189
|
+
raise "profile must be defined" if @profile.nil?
|
190
|
+
raise "project/workspace must be defined" if @filename.nil?
|
191
|
+
|
192
|
+
begin
|
193
|
+
project = Xcode.project @filename
|
194
|
+
@builder = project.target(@args[:target]).config(@args[:config]).builder
|
195
|
+
rescue
|
196
|
+
workspace = Xcode.workspace @filename
|
197
|
+
@builder = workspace.scheme(@args[:scheme]).builder
|
198
|
+
rescue
|
199
|
+
raise "You must provide a project or workspace"
|
200
|
+
end
|
201
|
+
|
202
|
+
raise "Could not create a builder using #{@args}" if @builder.nil?
|
203
|
+
|
204
|
+
unless @platform.nil?
|
205
|
+
builder.sdk = @platform.sdk
|
206
|
+
end
|
207
|
+
|
208
|
+
unless @identity.nil?
|
209
|
+
builder.identity = @identity
|
210
|
+
end
|
211
|
+
|
212
|
+
unless @keychain.nil?
|
213
|
+
keychain = Xcode::Keychain.new @keychain[:path]
|
214
|
+
keychain.unlock @keychain[:password] unless @keychain[:password].nil?
|
215
|
+
|
216
|
+
builder.identity = keychain.identities.first
|
217
|
+
builder.keychain = keychain
|
218
|
+
end
|
219
|
+
|
220
|
+
builder.profile = @profile
|
221
|
+
|
222
|
+
@before.call builder
|
223
|
+
|
224
|
+
@builder
|
225
|
+
end
|
226
|
+
|
227
|
+
def project_name
|
228
|
+
builder.product_name
|
229
|
+
end
|
230
|
+
|
231
|
+
#
|
232
|
+
# Create a set of rake tasks for this buildspec
|
233
|
+
#
|
234
|
+
def generate_rake_tasks
|
235
|
+
require 'socket'
|
236
|
+
|
237
|
+
# namespace project_name.downcase do
|
238
|
+
|
239
|
+
desc "Clean #{project_name}"
|
240
|
+
task :clean do
|
241
|
+
builder.clean
|
242
|
+
end
|
243
|
+
|
244
|
+
desc "Fetch dependencies for #{project_name}"
|
245
|
+
task :deps do
|
246
|
+
builder.dependencies
|
247
|
+
end
|
248
|
+
|
249
|
+
desc "Build #{project_name}"
|
250
|
+
task :build => [:clean, :deps] do
|
251
|
+
builder.config.info_plist do |info|
|
252
|
+
info.version = @build_number.call
|
253
|
+
info.save
|
254
|
+
end
|
255
|
+
builder.build
|
256
|
+
end
|
257
|
+
|
258
|
+
desc "Test #{project_name}"
|
259
|
+
task :test => [:deps] do
|
260
|
+
builder.test
|
261
|
+
end
|
262
|
+
|
263
|
+
desc "Package (.ipa & .dSYM.zip) #{project_name}"
|
264
|
+
task :package => [:build, :test] do
|
265
|
+
builder.package
|
266
|
+
end
|
267
|
+
|
268
|
+
namespace :deploy do
|
269
|
+
@deployments.each do |deployment|
|
270
|
+
desc "Deploy #{project_name} to #{deployment[:type]}"
|
271
|
+
task deployment[:type] => [:package] do
|
272
|
+
builder.deploy deployment[:type], deployment[:args]
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
desc "Deploy #{project_name} to all"
|
277
|
+
task :all => [:package]+(@deployments.map{|k,v| k[:type]}) do
|
278
|
+
puts "Deployed to all"
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
# end
|
283
|
+
end
|
284
|
+
|
285
|
+
end
|
286
|
+
end
|