xcodebuild-rb 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.md ADDED
@@ -0,0 +1,11 @@
1
+ # xcodebuild-rb changes
2
+
3
+ ## 0.2.0
4
+ * Added archive task
5
+ * Support Ruby 1.8.7
6
+ * Added after_(build|clean|archive) hooks for rake tasks
7
+ * Support capturing environment variables when a Run Script Phase with 'Show Environment Variables' is added.
8
+ * Ensure rake tasks exit with an appropriate exit code when the builds fail.
9
+
10
+ ## 0.1.0
11
+ * Initial release.
data/README.md CHANGED
@@ -15,7 +15,7 @@ After installing the gem, you need to create a `Rakefile` in the root of your pr
15
15
  A simple Rakefile will look like this:
16
16
 
17
17
  require 'rubygems'
18
- require 'xcodebuild-rb'
18
+ require 'xcodebuild'
19
19
 
20
20
  XcodeBuild::Tasks::BuildTask.new
21
21
 
@@ -35,6 +35,8 @@ When you run `rake xcode:build`, `xcodebuild` will be invoked without any argume
35
35
  t.configuration = "Release"
36
36
  end
37
37
 
38
+ _Note that in order to be able to use the `xcode:archive` task, a scheme **has** to be provided_
39
+
38
40
  When you run the rake tasks provided, the default behaviour is to simply output the exact output from `xcodebuild`. However, `xcodebuild-rb` can go one better and allow you to configure custom formatters that change the way the build output is displayed. Some formatters are built-in, or you can write your own.
39
41
 
40
42
  For instance, we could use the "progress" formatter that ships with `xcodebuild-rb`. Anybody who is used to the output of Ruby's Test::Unit library or RSpec library will be familiar with this.
@@ -56,4 +58,4 @@ Now when you run your build, your output will look something like this:
56
58
 
57
59
  ## License
58
60
 
59
- This library is licensed under the [MIT license](http://en.wikipedia.org/wiki/MIT_License).
61
+ This library is licensed under the [MIT license](http://en.wikipedia.org/wiki/MIT_License).
data/bin/rbxcb CHANGED
@@ -1,3 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
- require_relative "../lib/xcodebuild"
2
+
3
+ if __FILE__ == $0
4
+ require 'rubygems'
5
+ $:.unshift(File.expand_path('../../lib', __FILE__))
6
+ end
7
+
8
+ require "xcode_build"
3
9
  XcodeBuild.run(ARGV[0] || "")
data/lib/xcode_build.rb CHANGED
@@ -13,14 +13,17 @@ module XcodeBuild
13
13
  rescue EOFError
14
14
  end
15
15
  end
16
+
17
+ $?.exitstatus
16
18
  end
17
19
  end
18
20
 
19
- require_relative "xcode_build/build_action"
20
- require_relative "xcode_build/build_step"
21
- require_relative 'xcode_build/output_translator'
22
- require_relative 'xcode_build/reporter'
23
- require_relative 'xcode_build/formatters'
21
+ require 'xcode_build/build_action'
22
+ require 'xcode_build/build_step'
23
+ require 'xcode_build/output_translator'
24
+ require 'xcode_build/reporter'
25
+ require 'xcode_build/formatters'
26
+ require 'xcode_build/tasks'
24
27
 
25
28
  # configure the default translations for general use
26
29
  XcodeBuild::OutputTranslator.use_translation(:building)
@@ -1,6 +1,6 @@
1
1
  require 'state_machine'
2
2
 
3
- require_relative "build_step"
3
+ require "xcode_build/build_step"
4
4
 
5
5
  module XcodeBuild
6
6
  class BuildAction
@@ -47,11 +47,19 @@ module XcodeBuild
47
47
  def finished?
48
48
  successful? || failed?
49
49
  end
50
+
51
+ def has_errors?
52
+ failed_steps.any?
53
+ end
50
54
 
51
55
  def duration
52
56
  return nil unless finished?
53
57
  @finished_at - @started_at
54
58
  end
59
+
60
+ def label
61
+ @label.downcase.capitalize
62
+ end
55
63
 
56
64
  def project_name
57
65
  @metadata[:project]
@@ -69,4 +77,4 @@ module XcodeBuild
69
77
  @metadata[:default]
70
78
  end
71
79
  end
72
- end
80
+ end
@@ -11,6 +11,7 @@ module XcodeBuild
11
11
  end
12
12
 
13
13
  def add_error(params)
14
+ @failed = true
14
15
  @errors << Error.new(params)
15
16
  end
16
17
 
@@ -42,6 +43,13 @@ module XcodeBuild
42
43
  private
43
44
 
44
45
  class Error < OpenStruct
46
+ def error_detail
47
+ if self.file
48
+ "in #{err.file}:#{err.line.to_s}"
49
+ elsif self.command
50
+ "#{self.command} failed with exit code #{self.exit_code}"
51
+ end
52
+ end
45
53
  end
46
54
  end
47
55
  end
@@ -3,4 +3,4 @@ module XcodeBuild
3
3
  end
4
4
  end
5
5
 
6
- require_relative "formatters/progress_formatter"
6
+ require "xcode_build/formatters/progress_formatter"
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
- require_relative '../utilities/colorize'
2
+ require 'xcode_build/utilities/colorize'
3
3
 
4
4
  module XcodeBuild
5
5
  module Formatters
@@ -19,7 +19,7 @@ module XcodeBuild
19
19
  end
20
20
 
21
21
  def clean_finished(clean)
22
- report_finished("Clean", clean)
22
+ report_finished(clean)
23
23
  end
24
24
 
25
25
  def build_started(build)
@@ -31,30 +31,34 @@ module XcodeBuild
31
31
  end
32
32
 
33
33
  def build_finished(build)
34
- report_finished("Build", build)
34
+ report_finished(build)
35
35
  end
36
36
 
37
- def report_finished(type, object)
37
+ def report_finished(object)
38
38
  puts
39
39
  puts
40
40
  puts "Finished in #{object.duration} seconds."
41
41
 
42
42
  if object.successful?
43
- puts green("#{type} succeeded.")
43
+ puts green("#{object.label} succeeded.")
44
44
  else
45
- puts red("#{type} failed.")
45
+ puts red("#{object.label} failed.")
46
46
  puts
47
- puts "Failed #{type.downcase} steps:"
47
+ puts "Failed #{object.label.downcase} steps:"
48
48
  puts
49
49
  error_counter = 1
50
50
  object.steps_completed.each do |step|
51
51
  next unless step.has_errors?
52
-
52
+
53
53
  puts indent("#{error_counter}) #{step.type} #{step.arguments.join(" ")}")
54
54
 
55
55
  step.errors.each do |err|
56
- puts indent(" #{red(err.message)}")
57
- puts indent(cyan(" in #{err.file}:#{err.line.to_s}")) if err.file
56
+ print indent(" #{red(err.message)}")
57
+ if err.error_detail
58
+ puts indent(cyan(err.error_detail))
59
+ else
60
+ puts
61
+ end
58
62
  puts
59
63
  end
60
64
 
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
- require_relative 'translations'
2
+ require 'xcode_build/translations'
3
3
 
4
4
  module XcodeBuild
5
5
  class OutputTranslator
@@ -13,7 +13,7 @@ module XcodeBuild
13
13
  end
14
14
 
15
15
  def <<(line)
16
- notify_delegate(:beginning_translation_of_line, args: line)
16
+ notify_delegate(:beginning_translation_of_line, :args => line)
17
17
  translations.each { |translation| translation.attempt_to_translate(line) }
18
18
  end
19
19
 
@@ -56,7 +56,8 @@ module XcodeBuild
56
56
  private
57
57
 
58
58
  module TranslationHelpers
59
- def notify_delegate(message, options = {args: []})
59
+ def notify_delegate(message, options = {})
60
+ options[:args] ||= []
60
61
  if @delegate.respond_to?(message)
61
62
  @delegate.send(message, *options[:args])
62
63
  else
@@ -1,5 +1,5 @@
1
- require_relative "reporting/build_reporting"
2
- require_relative "reporting/clean_reporting"
1
+ require "xcode_build/reporting/build_reporting"
2
+ require "xcode_build/reporting/clean_reporting"
3
3
 
4
4
  module XcodeBuild
5
5
  class Reporter
@@ -11,6 +11,14 @@ module XcodeBuild
11
11
  def initialize(delegate = nil)
12
12
  @delegate = delegate
13
13
  end
14
+
15
+ def direct_raw_output_to=(stream)
16
+ @output_stream = stream
17
+ end
18
+
19
+ def beginning_translation_of_line(line)
20
+ (@output_stream << line) if @output_stream
21
+ end
14
22
 
15
23
  private
16
24
 
@@ -11,35 +11,48 @@ module XcodeBuild
11
11
 
12
12
  def build_started(params)
13
13
  @build = Build.new(params)
14
- notify :build_started, @build
14
+ notify :build_started, build
15
15
  end
16
16
 
17
17
  def build_step(params)
18
- if @build.last_step
19
- notify :build_step_finished, @build.last_step
18
+ if build.last_step
19
+ notify :build_step_finished, build.last_step
20
20
  end
21
21
 
22
- @build.add_step(params)
22
+ build.add_step(params)
23
23
 
24
- notify :build_step_started, @build.last_step
24
+ notify :build_step_started, build.last_step
25
25
  end
26
26
 
27
27
  def build_error_detected(params)
28
- @build.last_step.add_error(params)
28
+ build.last_step.add_error(params)
29
+ end
30
+
31
+ def build_env_variable_detected(key, value)
32
+ build.set_environment_variable(key, value)
29
33
  end
30
34
 
31
- def build_succeeded
32
- @build.success!
35
+ def build_succeeded(archive_or_build)
36
+ build.label = archive_or_build
37
+
38
+ # for some reason, archive reports a success even if there was an error
39
+ if build.has_errors?
40
+ build.failure!
41
+ else
42
+ build.success!
43
+ end
44
+
33
45
  build_finished
34
46
  end
35
47
 
36
- def build_failed
37
- @build.failure!
48
+ def build_failed(archive_or_build)
49
+ build.label = archive_or_build
50
+ build.failure!
38
51
  build_finished
39
52
  end
40
53
 
41
54
  def build_step_failed(params)
42
- if step = @build.step_with_params(params)
55
+ if step = build.step_with_params(params)
43
56
  step.failed = true
44
57
  end
45
58
  end
@@ -47,14 +60,30 @@ module XcodeBuild
47
60
  private
48
61
 
49
62
  def build_finished
50
- if @build.last_step
51
- notify :build_step_finished, @build.last_step
63
+ if build.last_step
64
+ notify :build_step_finished, build.last_step
52
65
  end
53
66
 
54
- notify :build_finished, @build
67
+ notify :build_finished, build
55
68
  end
56
69
 
57
70
  class Build < BuildAction
71
+ attr_reader :environment
72
+ attr_writer :label
73
+
74
+ def initialize(metadata)
75
+ super(metadata)
76
+ @environment = {}
77
+ @label = "Build"
78
+ end
79
+
80
+ def set_environment_variable(key, value)
81
+ @environment[key] = value
82
+ end
83
+
84
+ def target_build_directory
85
+ @environment["TARGET_BUILD_DIR"]
86
+ end
58
87
  end
59
88
  end
60
89
  end
@@ -9,7 +9,7 @@ module XcodeBuild
9
9
 
10
10
  def clean_started(params)
11
11
  @clean = Clean.new(params)
12
- notify :clean_started, @clean
12
+ notify :clean_started, clean
13
13
  end
14
14
 
15
15
  def clean_step(params)
@@ -53,6 +53,10 @@ module XcodeBuild
53
53
  end
54
54
 
55
55
  class Clean < BuildAction
56
+ def initialize(metadata)
57
+ super(metadata)
58
+ @label = "Clean"
59
+ end
56
60
  end
57
61
  end
58
62
  end
@@ -0,0 +1,6 @@
1
+ module XcodeBuild
2
+ module Tasks
3
+ end
4
+ end
5
+
6
+ require 'xcode_build/tasks/build_task'
@@ -3,6 +3,8 @@ require 'rake/tasklib'
3
3
  module XcodeBuild
4
4
  module Tasks
5
5
  class BuildTask < ::Rake::TaskLib
6
+ include Rake::DSL if defined?(Rake::DSL)
7
+
6
8
  attr_accessor :project_name
7
9
  attr_accessor :target
8
10
  attr_accessor :workspace
@@ -14,16 +16,36 @@ module XcodeBuild
14
16
  attr_accessor :output_to
15
17
  attr_accessor :formatter
16
18
  attr_accessor :invoke_from_within
17
- attr_accessor :reporter
19
+ attr_accessor :reporter_klass
18
20
 
19
21
  def initialize(namespace = :xcode, &block)
20
22
  @namespace = namespace
21
23
  @output_to = STDOUT
22
24
  @invoke_from_within = "."
25
+ @reporter_klass = XcodeBuild::Reporter
26
+ @hooks = {}
23
27
 
24
28
  yield self if block_given?
25
29
  define
26
30
  end
31
+
32
+ def after_build(&block)
33
+ @hooks[:after_build] = block
34
+ end
35
+
36
+ def after_clean(&block)
37
+ @hooks[:after_clean] = block
38
+ end
39
+
40
+ def after_archive(&block)
41
+ @hooks[:after_archive] = block
42
+ end
43
+
44
+ def execute_hook(name, *args)
45
+ if hook = @hooks[name.to_sym]
46
+ hook.call(*args)
47
+ end
48
+ end
27
49
 
28
50
  def run(task)
29
51
  Rake::Task["#{@namespace}:#{task}"].invoke
@@ -41,44 +63,72 @@ module XcodeBuild
41
63
  opts << "-xcconfig #{xcconfig}" if xcconfig
42
64
  end
43
65
  end
66
+
67
+ def reporter
68
+ @reporter ||= @reporter_klass.new(formatter)
69
+ end
44
70
 
45
71
  private
46
72
 
47
73
  def output_buffer
48
- if reporter
49
- XcodeBuild::OutputTranslator.new(reporter)
50
- elsif formatter
51
- default_reporter = XcodeBuild::Reporter.new(formatter)
52
- XcodeBuild::OutputTranslator.new(default_reporter)
53
- else
54
- output_to
55
- end
74
+ @output_buffer ||= XcodeBuild::OutputTranslator.new(reporter)
56
75
  end
57
76
 
58
77
  def build_opts_string(*additional_opts)
59
- (build_opts + additional_opts).join(" ")
78
+ (build_opts + additional_opts).compact.join(" ")
79
+ end
80
+
81
+ def xcodebuild(action)
82
+ reporter.direct_raw_output_to = output_to unless formatter
83
+
84
+ status = Dir.chdir(invoke_from_within) do
85
+ XcodeBuild.run(build_opts_string(action), output_buffer)
86
+ end
87
+
88
+ check_status(status)
89
+
90
+ if reporter.build && reporter.build.failed?
91
+ # sometimes, a build/archive can fail and xcodebuild won't return a non-zero code
92
+ raise "xcodebuild failed (#{reporter.build.failed_steps.length} steps failed)"
93
+ end
94
+
95
+ case action
96
+ when :build
97
+ execute_hook(:after_build, reporter.build)
98
+ when :archive
99
+ execute_hook(:after_build, reporter.build)
100
+ execute_hook(:after_archive, reporter.build)
101
+ when :clean
102
+ execute_hook(:after_clean, reporter.clean)
103
+ end
60
104
  end
61
105
 
62
106
  def define
63
107
  namespace(@namespace) do
108
+ desc "Creates an archive build of the specified target(s)."
109
+ task :archive do
110
+ raise "You need to specify a `scheme' in order to be able to create an archive build!" unless scheme
111
+ xcodebuild :archive
112
+ end
113
+
64
114
  desc "Builds the specified target(s)."
65
115
  task :build do
66
- Dir.chdir(invoke_from_within) do
67
- XcodeBuild.run(build_opts_string, output_buffer)
68
- end
116
+ xcodebuild :build
69
117
  end
70
118
 
71
119
  desc "Cleans the build using the same build settings."
72
120
  task :clean do
73
- Dir.chdir(invoke_from_within) do
74
- XcodeBuild.run(build_opts_string("clean"), output_buffer)
75
- end
121
+ xcodebuild :clean
76
122
  end
77
123
 
78
124
  desc "Builds the specified target(s) from a clean slate."
79
125
  task :cleanbuild => [:clean, :build]
80
126
  end
81
127
  end
128
+
129
+ def check_status(status)
130
+ raise "xcodebuild failed (exited with status: #{status})" unless status == 0
131
+ end
82
132
  end
83
133
  end
84
134
  end