xcodebuild-rb 0.1.0 → 0.2.0

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/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