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.
Files changed (47) hide show
  1. data/.gitignore +6 -0
  2. data/.rbenv-version +1 -0
  3. data/.rvmrc +1 -1
  4. data/Gemfile +10 -2
  5. data/README.md +110 -9
  6. data/Rakefile +2 -2
  7. data/bin/xcoder +74 -0
  8. data/lib/xcode/builder.rb +1 -2
  9. data/lib/xcode/builder/base_builder.rb +231 -102
  10. data/lib/xcode/builder/build_parser.rb +146 -0
  11. data/lib/xcode/builder/project_target_config_builder.rb +2 -2
  12. data/lib/xcode/builder/scheme_builder.rb +29 -12
  13. data/lib/xcode/buildspec.rb +286 -0
  14. data/lib/xcode/configuration_list.rb +24 -24
  15. data/lib/xcode/deploy/ftp.rb +56 -0
  16. data/lib/xcode/deploy/kickfolio.rb +18 -0
  17. data/lib/xcode/deploy/s3.rb +38 -0
  18. data/lib/xcode/deploy/ssh.rb +43 -0
  19. data/lib/xcode/deploy/templates/index.rhtml +22 -0
  20. data/lib/xcode/deploy/templates/manifest.rhtml +31 -0
  21. data/lib/xcode/deploy/testflight.rb +32 -27
  22. data/lib/xcode/deploy/web_assets.rb +39 -0
  23. data/lib/xcode/info_plist.rb +16 -0
  24. data/lib/xcode/keychain.rb +33 -10
  25. data/lib/xcode/platform.rb +65 -0
  26. data/lib/xcode/project.rb +7 -3
  27. data/lib/xcode/provisioning_profile.rb +38 -2
  28. data/lib/xcode/scheme.rb +44 -17
  29. data/lib/xcode/shell/command.rb +79 -5
  30. data/lib/xcode/terminal_output.rb +116 -0
  31. data/lib/xcode/test/formatters/junit_formatter.rb +7 -2
  32. data/lib/xcode/test/formatters/stdout_formatter.rb +34 -25
  33. data/lib/xcode/test/parsers/kif_parser.rb +87 -0
  34. data/lib/xcode/test/parsers/ocunit_parser.rb +3 -3
  35. data/lib/xcode/version.rb +1 -1
  36. data/lib/xcode/workspace.rb +13 -5
  37. data/lib/xcoder.rb +33 -31
  38. data/spec/TestProject/TestProject.xcodeproj/project.pbxproj +1627 -1015
  39. data/spec/TestWorkspace.xcworkspace/contents.xcworkspacedata +7 -0
  40. data/spec/builder_spec.rb +87 -71
  41. data/spec/deploy_spec.rb +63 -0
  42. data/spec/ocunit_parser_spec.rb +1 -1
  43. data/xcoder.gemspec +3 -1
  44. metadata +95 -19
  45. data/lib/xcode/buildfile.rb +0 -101
  46. data/lib/xcode/shell.rb +0 -26
  47. 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 xcodebuild
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
- super @scheme.build_targets.last, @scheme.build_config
19
+ @scheme = scheme
20
+ @target = @scheme.build_targets.last
21
+ super @target, @target.config(@scheme.archive_config)
21
22
  end
22
-
23
- def xcodebuild
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