autobuild 1.17.0 → 1.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +107 -0
- data/Gemfile +2 -1
- data/Rakefile +1 -4
- data/autobuild.gemspec +14 -11
- data/bin/autobuild +4 -3
- data/lib/autobuild.rb +4 -5
- data/lib/autobuild/build_logfile.rb +6 -4
- data/lib/autobuild/config.rb +90 -40
- data/lib/autobuild/configurable.rb +30 -18
- data/lib/autobuild/environment.rb +126 -120
- data/lib/autobuild/exceptions.rb +48 -31
- data/lib/autobuild/import/archive.rb +134 -82
- data/lib/autobuild/import/cvs.rb +28 -24
- data/lib/autobuild/import/darcs.rb +13 -16
- data/lib/autobuild/import/git-lfs.rb +37 -30
- data/lib/autobuild/import/git.rb +231 -179
- data/lib/autobuild/import/hg.rb +23 -18
- data/lib/autobuild/import/svn.rb +48 -29
- data/lib/autobuild/importer.rb +530 -499
- data/lib/autobuild/mail_reporter.rb +77 -77
- data/lib/autobuild/package.rb +171 -101
- data/lib/autobuild/packages/autotools.rb +47 -42
- data/lib/autobuild/packages/cmake.rb +71 -65
- data/lib/autobuild/packages/dummy.rb +9 -8
- data/lib/autobuild/packages/genom.rb +1 -1
- data/lib/autobuild/packages/gnumake.rb +19 -13
- data/lib/autobuild/packages/import.rb +2 -6
- data/lib/autobuild/packages/orogen.rb +32 -31
- data/lib/autobuild/packages/pkgconfig.rb +2 -2
- data/lib/autobuild/packages/python.rb +7 -2
- data/lib/autobuild/packages/ruby.rb +22 -17
- data/lib/autobuild/parallel.rb +35 -39
- data/lib/autobuild/pkgconfig.rb +25 -13
- data/lib/autobuild/progress_display.rb +23 -23
- data/lib/autobuild/rake_task_extension.rb +6 -6
- data/lib/autobuild/reporting.rb +38 -26
- data/lib/autobuild/subcommand.rb +72 -65
- data/lib/autobuild/test.rb +8 -7
- data/lib/autobuild/test_utility.rb +10 -9
- data/lib/autobuild/timestamps.rb +28 -23
- data/lib/autobuild/tools.rb +17 -16
- data/lib/autobuild/utility.rb +16 -18
- data/lib/autobuild/version.rb +1 -1
- metadata +39 -38
data/lib/autobuild/pkgconfig.rb
CHANGED
@@ -2,10 +2,16 @@
|
|
2
2
|
class PkgConfig
|
3
3
|
class NotFound < RuntimeError
|
4
4
|
attr_reader :name
|
5
|
-
|
6
|
-
def
|
5
|
+
|
6
|
+
def initialize(name)
|
7
|
+
@name = name
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"#{name} is not available to pkg-config"
|
12
|
+
end
|
7
13
|
end
|
8
|
-
|
14
|
+
|
9
15
|
# The module name
|
10
16
|
attr_reader :name
|
11
17
|
# The module version
|
@@ -14,30 +20,36 @@ def to_s; "#{name} is not available to pkg-config" end
|
|
14
20
|
# Create a PkgConfig object for the package +name+
|
15
21
|
# Raises PkgConfig::NotFound if the module does not exist
|
16
22
|
def initialize(name)
|
17
|
-
|
18
|
-
raise NotFound.new(name)
|
23
|
+
unless system("pkg-config --exists #{name}")
|
24
|
+
raise NotFound.new(name), "pkg-config package '#{name}' not found"
|
19
25
|
end
|
20
|
-
|
26
|
+
|
21
27
|
@name = name
|
22
28
|
@version = `pkg-config --modversion #{name}`.chomp.strip
|
23
29
|
@actions = Hash.new
|
24
30
|
@variables = Hash.new
|
25
31
|
end
|
26
32
|
|
27
|
-
ACTIONS = %w
|
28
|
-
|
33
|
+
ACTIONS = %w[cflags cflags-only-I cflags-only-other
|
34
|
+
libs libs-only-L libs-only-l libs-only-other static].freeze
|
29
35
|
ACTIONS.each do |action|
|
30
|
-
define_method(action.
|
36
|
+
define_method(action.tr('-', '_')) do
|
31
37
|
@actions[action] ||= `pkg-config --#{action} #{name}`.chomp.strip
|
32
38
|
end
|
33
39
|
end
|
34
40
|
|
41
|
+
def respond_to_missing?(varname, _include_all)
|
42
|
+
varname =~ /^\w+$/
|
43
|
+
end
|
44
|
+
|
35
45
|
def method_missing(varname, *args, &proc)
|
36
46
|
if args.empty?
|
37
|
-
@variables[varname]
|
38
|
-
|
39
|
-
|
47
|
+
unless (value = @variables[varname])
|
48
|
+
value = `pkg-config --variable=#{varname} #{name}`.chomp.strip
|
49
|
+
@variables[varname] = value
|
50
|
+
end
|
51
|
+
return value
|
40
52
|
end
|
53
|
+
super
|
41
54
|
end
|
42
55
|
end
|
43
|
-
|
@@ -3,7 +3,6 @@ module Autobuild
|
|
3
3
|
class ProgressDisplay
|
4
4
|
def initialize(io, color: ::Autobuild.method(:color))
|
5
5
|
@io = io
|
6
|
-
#@cursor = Blank.new
|
7
6
|
@cursor = TTY::Cursor
|
8
7
|
@last_formatted_progress = []
|
9
8
|
@progress_messages = []
|
@@ -21,7 +20,8 @@ def silent?
|
|
21
20
|
end
|
22
21
|
|
23
22
|
def silent
|
24
|
-
@silent
|
23
|
+
@silent = true
|
24
|
+
silent = @silent
|
25
25
|
yield
|
26
26
|
ensure
|
27
27
|
@silent = silent
|
@@ -36,12 +36,11 @@ def progress_enabled?
|
|
36
36
|
def message(message, *args, io: @io, force: false)
|
37
37
|
return if silent? && !force
|
38
38
|
|
39
|
-
if args.last.respond_to?(:to_io)
|
40
|
-
io = args.pop
|
41
|
-
end
|
39
|
+
io = args.pop if args.last.respond_to?(:to_io)
|
42
40
|
|
43
41
|
@display_lock.synchronize do
|
44
|
-
io.print "#{@cursor.column(1)}#{@cursor.clear_screen_down}
|
42
|
+
io.print "#{@cursor.column(1)}#{@cursor.clear_screen_down}"\
|
43
|
+
"#{@color.call(message, *args)}\n"
|
45
44
|
io.flush if @io != io
|
46
45
|
display_progress
|
47
46
|
@io.flush
|
@@ -94,9 +93,7 @@ def progress_done(key, display_last = true, message: nil)
|
|
94
93
|
current_size = @progress_messages.size
|
95
94
|
@progress_messages.delete_if do |msg_key, msg|
|
96
95
|
if msg_key == key
|
97
|
-
if display_last && !message
|
98
|
-
message = msg
|
99
|
-
end
|
96
|
+
message = msg if display_last && !message
|
100
97
|
true
|
101
98
|
end
|
102
99
|
end
|
@@ -116,11 +113,11 @@ def progress_done(key, display_last = true, message: nil)
|
|
116
113
|
end
|
117
114
|
end
|
118
115
|
|
119
|
-
|
120
116
|
def display_progress
|
121
117
|
return unless progress_enabled?
|
122
118
|
|
123
|
-
formatted = format_grouped_messages(@progress_messages.map(&:last),
|
119
|
+
formatted = format_grouped_messages(@progress_messages.map(&:last),
|
120
|
+
indent: " ")
|
124
121
|
@io.print @cursor.clear_screen_down
|
125
122
|
@io.print formatted.join("\n")
|
126
123
|
if formatted.size > 1
|
@@ -137,13 +134,11 @@ def find_common_prefix(msg, other_msg)
|
|
137
134
|
msg.each_with_index do |token, idx|
|
138
135
|
if other_msg[idx] != token
|
139
136
|
prefix = msg[0..(idx - 1)].join(" ")
|
140
|
-
|
141
|
-
prefix << " "
|
142
|
-
end
|
137
|
+
prefix << " " unless prefix.empty?
|
143
138
|
return prefix
|
144
139
|
end
|
145
140
|
end
|
146
|
-
|
141
|
+
msg.join(" ")
|
147
142
|
end
|
148
143
|
|
149
144
|
def group_messages(messages)
|
@@ -152,19 +147,22 @@ def group_messages(messages)
|
|
152
147
|
groups = Array.new
|
153
148
|
groups << ["", (0...messages.size)]
|
154
149
|
messages.each_with_index do |msg, idx|
|
155
|
-
prefix
|
150
|
+
prefix = nil
|
151
|
+
grouping = false
|
156
152
|
messages[(idx + 1)..-1].each_with_index do |other_msg, other_idx|
|
157
153
|
other_idx += idx + 1
|
158
154
|
prefix ||= find_common_prefix(msg, other_msg)
|
159
|
-
break
|
155
|
+
break unless other_msg.start_with?(prefix)
|
160
156
|
|
161
157
|
if grouping
|
162
158
|
break if prefix != groups.last[0]
|
159
|
+
|
163
160
|
groups.last[1] << other_idx
|
164
161
|
else
|
165
162
|
current_prefix, current_group = groups.last
|
166
|
-
if prefix.size > current_prefix.size # create a new group
|
167
|
-
|
163
|
+
if prefix.size > current_prefix.size # create a new group
|
164
|
+
group_end_index = [idx - 1, current_group.last].min
|
165
|
+
groups.last[1] = (current_group.first..group_end_index)
|
168
166
|
groups << [prefix, [idx, other_idx]]
|
169
167
|
grouping = true
|
170
168
|
else break
|
@@ -179,13 +177,14 @@ def group_messages(messages)
|
|
179
177
|
groups.map do |prefix, indexes|
|
180
178
|
indexes = indexes.to_a
|
181
179
|
next if indexes.empty?
|
180
|
+
|
182
181
|
range = (prefix.size)..-1
|
183
182
|
[prefix, indexes.map { |i| messages[i][range] }]
|
184
183
|
end.compact
|
185
184
|
end
|
186
185
|
|
187
|
-
def format_grouped_messages(
|
188
|
-
groups = group_messages(
|
186
|
+
def format_grouped_messages(raw_messages, indent: " ", width: TTY::Screen.width)
|
187
|
+
groups = group_messages(raw_messages)
|
189
188
|
groups.each_with_object([]) do |(prefix, messages), lines|
|
190
189
|
if prefix.empty?
|
191
190
|
lines.concat(messages.map { |m| "#{indent}#{m.strip}" })
|
@@ -197,10 +196,11 @@ def format_grouped_messages(messages, indent: " ", width: TTY::Screen.width)
|
|
197
196
|
msg = messages.shift.strip
|
198
197
|
margin = messages.empty? ? 1 : 2
|
199
198
|
if lines.last.size + margin + msg.size > width
|
200
|
-
lines << ""
|
199
|
+
lines.last << ","
|
200
|
+
lines << +""
|
201
201
|
lines.last << indent << indent << msg
|
202
202
|
else
|
203
|
-
lines.last << " " << msg
|
203
|
+
lines.last << ", " << msg
|
204
204
|
end
|
205
205
|
end
|
206
206
|
lines.last << "," unless messages.empty?
|
@@ -1,19 +1,19 @@
|
|
1
1
|
module Autobuild
|
2
2
|
module RakeTaskExtension
|
3
3
|
def already_invoked?
|
4
|
-
|
4
|
+
@already_invoked
|
5
5
|
end
|
6
6
|
|
7
|
-
|
8
|
-
@already_invoked = value
|
9
|
-
end
|
7
|
+
attr_writer :already_invoked
|
10
8
|
|
11
9
|
def disable!
|
12
10
|
@already_invoked = true
|
13
|
-
|
11
|
+
singleton_class.class_eval do
|
12
|
+
define_method(:needed?) { false }
|
13
|
+
end
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
17
|
-
class Rake::Task
|
17
|
+
class Rake::Task # rubocop:disable Style/ClassAndModuleChildren
|
18
18
|
include Autobuild::RakeTaskExtension
|
19
19
|
end
|
data/lib/autobuild/reporting.rb
CHANGED
@@ -16,6 +16,7 @@ def color=(flag)
|
|
16
16
|
def color?
|
17
17
|
@colorizer.enabled?
|
18
18
|
end
|
19
|
+
|
19
20
|
def color(message, *style)
|
20
21
|
@colorizer.decorate(message, *style)
|
21
22
|
end
|
@@ -31,6 +32,7 @@ class << self
|
|
31
32
|
def silent?
|
32
33
|
@display.silent?
|
33
34
|
end
|
35
|
+
|
34
36
|
def silent=(flag)
|
35
37
|
@display.silent = flag
|
36
38
|
end
|
@@ -80,7 +82,7 @@ def self.progress_done(key, display_last = true, message: nil)
|
|
80
82
|
#
|
81
83
|
# It does not use a logging framework like Log4r, but it should ;-)
|
82
84
|
module Reporting
|
83
|
-
|
85
|
+
@reporters = Array.new
|
84
86
|
|
85
87
|
## Run a block and report known exception
|
86
88
|
# If an exception is fatal, the program is terminated using exit()
|
@@ -89,18 +91,22 @@ def self.report(on_package_failures: default_report_on_package_failures)
|
|
89
91
|
rescue Interrupt => e
|
90
92
|
interrupted = e
|
91
93
|
rescue Autobuild::Exception => e
|
92
|
-
return report_finish_on_error([e],
|
94
|
+
return report_finish_on_error([e],
|
95
|
+
on_package_failures: on_package_failures,
|
96
|
+
interrupted_by: interrupted)
|
93
97
|
end
|
94
98
|
|
95
99
|
# If ignore_erorrs is true, check if some packages have failed
|
96
100
|
# on the way. If so, raise an exception to inform the user about
|
97
101
|
# it
|
98
102
|
errors = []
|
99
|
-
Autobuild::Package.each do |
|
103
|
+
Autobuild::Package.each do |_name, pkg|
|
100
104
|
errors.concat(pkg.failures)
|
101
105
|
end
|
102
106
|
|
103
|
-
report_finish_on_error(errors,
|
107
|
+
report_finish_on_error(errors,
|
108
|
+
on_package_failures: on_package_failures,
|
109
|
+
interrupted_by: interrupted)
|
104
110
|
end
|
105
111
|
|
106
112
|
# @api private
|
@@ -121,15 +127,18 @@ def self.default_report_on_package_failures
|
|
121
127
|
#
|
122
128
|
# @param [Symbol] on_package_failures how does the reporting should behave.
|
123
129
|
#
|
124
|
-
def self.report_finish_on_error(errors,
|
125
|
-
|
130
|
+
def self.report_finish_on_error(errors,
|
131
|
+
on_package_failures: default_report_on_package_failures, interrupted_by: nil)
|
132
|
+
if (not_package_error = errors.find { |e| !e.respond_to?(:fatal?) })
|
126
133
|
raise not_package_error
|
127
134
|
end
|
128
|
-
|
135
|
+
|
136
|
+
unless %i[raise report_silent exit_silent].include?(on_package_failures)
|
129
137
|
errors.each { |e| error(e) }
|
130
138
|
end
|
139
|
+
|
131
140
|
fatal = errors.any?(&:fatal?)
|
132
|
-
|
141
|
+
unless fatal
|
133
142
|
if interrupted_by
|
134
143
|
raise interrupted_by
|
135
144
|
else
|
@@ -138,30 +147,29 @@ def self.report_finish_on_error(errors, on_package_failures: default_report_on_p
|
|
138
147
|
end
|
139
148
|
|
140
149
|
if on_package_failures == :raise
|
141
|
-
if interrupted_by
|
142
|
-
raise interrupted_by
|
143
|
-
end
|
150
|
+
raise interrupted_by if interrupted_by
|
144
151
|
|
145
152
|
e = if errors.size == 1 then errors.first
|
146
|
-
|
147
|
-
|
153
|
+
else CompositeException.new(errors)
|
154
|
+
end
|
148
155
|
raise e
|
149
|
-
elsif [
|
156
|
+
elsif %i[report_silent report].include?(on_package_failures)
|
150
157
|
if interrupted_by
|
151
158
|
raise interrupted_by
|
152
159
|
else
|
153
160
|
return errors
|
154
161
|
end
|
155
|
-
elsif [
|
162
|
+
elsif %i[exit exit_silent].include?(on_package_failures)
|
156
163
|
exit 1
|
157
164
|
else
|
158
|
-
raise ArgumentError, "unexpected value for on_package_failures:
|
165
|
+
raise ArgumentError, "unexpected value for on_package_failures: "\
|
166
|
+
"#{on_package_failures}"
|
159
167
|
end
|
160
168
|
end
|
161
169
|
|
162
170
|
## Reports a successful build to the user
|
163
171
|
def self.success
|
164
|
-
each_reporter
|
172
|
+
each_reporter(&:success)
|
165
173
|
end
|
166
174
|
|
167
175
|
## Reports that the build failed to the user
|
@@ -171,19 +179,19 @@ def self.error(error)
|
|
171
179
|
|
172
180
|
## Add a new reporter
|
173
181
|
def self.<<(reporter)
|
174
|
-
|
182
|
+
@reporters << reporter
|
175
183
|
end
|
176
184
|
|
177
185
|
def self.remove(reporter)
|
178
|
-
|
186
|
+
@reporters.delete(reporter)
|
179
187
|
end
|
180
188
|
|
181
189
|
def self.clear_reporters
|
182
|
-
|
190
|
+
@reporters.clear
|
183
191
|
end
|
184
192
|
|
185
193
|
def self.each_reporter(&iter)
|
186
|
-
|
194
|
+
@reporters.each(&iter)
|
187
195
|
end
|
188
196
|
|
189
197
|
## Iterate on all log files
|
@@ -195,6 +203,7 @@ def self.each_log(&block)
|
|
195
203
|
## Base class for reporters
|
196
204
|
class Reporter
|
197
205
|
def error(error); end
|
206
|
+
|
198
207
|
def success; end
|
199
208
|
end
|
200
209
|
|
@@ -203,11 +212,10 @@ class StdoutReporter < Reporter
|
|
203
212
|
def error(error)
|
204
213
|
STDERR.puts "Build failed: #{error}"
|
205
214
|
end
|
215
|
+
|
206
216
|
def success
|
207
217
|
puts "Build finished successfully at #{Time.now}"
|
208
|
-
if Autobuild.post_success_message
|
209
|
-
puts Autobuild.post_success_message
|
210
|
-
end
|
218
|
+
puts Autobuild.post_success_message if Autobuild.post_success_message
|
211
219
|
end
|
212
220
|
end
|
213
221
|
|
@@ -216,12 +224,16 @@ def success
|
|
216
224
|
[1_000_000.0, "M"],
|
217
225
|
[1_000.0, "k"],
|
218
226
|
[1.0, ""]
|
219
|
-
]
|
227
|
+
].freeze
|
220
228
|
|
221
229
|
def self.human_readable_size(size)
|
222
230
|
HUMAN_READABLE_SIZES.each do |scale, name|
|
223
231
|
scaled_size = (size / scale)
|
224
|
-
|
232
|
+
if scaled_size > 1
|
233
|
+
return format("%3.1<scaled>f%<scale_name>s",
|
234
|
+
scaled: scaled_size,
|
235
|
+
scale_name: name)
|
236
|
+
end
|
225
237
|
end
|
226
238
|
end
|
227
239
|
end
|
data/lib/autobuild/subcommand.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
require 'autobuild/exceptions'
|
3
3
|
require 'autobuild/reporting'
|
4
4
|
require 'fcntl'
|
5
|
+
require 'English'
|
5
6
|
|
6
7
|
module Autobuild
|
7
8
|
@logfiles = Set.new
|
@@ -24,9 +25,11 @@ def self.registered_logfile?(logfile)
|
|
24
25
|
def self.statistics
|
25
26
|
@statistics
|
26
27
|
end
|
28
|
+
|
27
29
|
def self.reset_statistics
|
28
30
|
@statistics = Hash.new
|
29
31
|
end
|
32
|
+
|
30
33
|
def self.add_stat(package, phase, duration)
|
31
34
|
if !@statistics[package]
|
32
35
|
@statistics[package] = { phase => duration }
|
@@ -36,6 +39,7 @@ def self.add_stat(package, phase, duration)
|
|
36
39
|
@statistics[package][phase] += duration
|
37
40
|
end
|
38
41
|
end
|
42
|
+
|
39
43
|
reset_statistics
|
40
44
|
|
41
45
|
@parallel_build_level = nil
|
@@ -52,9 +56,8 @@ class << self
|
|
52
56
|
def displayed_error_line_count=(value)
|
53
57
|
@displayed_error_line_count = validate_displayed_error_line_count(value)
|
54
58
|
end
|
55
|
-
|
56
|
-
|
57
|
-
end
|
59
|
+
|
60
|
+
attr_reader :displayed_error_line_count
|
58
61
|
|
59
62
|
# Returns the number of processes that can run in parallel during the
|
60
63
|
# build. This is a system-wide value that can be overriden in a
|
@@ -78,13 +81,13 @@ def parallel_build_level
|
|
78
81
|
|
79
82
|
# Returns the number of CPUs present on this system
|
80
83
|
def self.autodetect_processor_count
|
81
|
-
if @processor_count
|
82
|
-
return @processor_count
|
83
|
-
end
|
84
|
+
return @processor_count if @processor_count
|
84
85
|
|
85
86
|
if File.file?('/proc/cpuinfo')
|
86
87
|
cpuinfo = File.readlines('/proc/cpuinfo')
|
87
|
-
physical_ids
|
88
|
+
physical_ids = []
|
89
|
+
core_count = []
|
90
|
+
processor_ids = []
|
88
91
|
cpuinfo.each do |line|
|
89
92
|
case line
|
90
93
|
when /^processor\s+:\s+(\d+)$/
|
@@ -99,7 +102,10 @@ def self.autodetect_processor_count
|
|
99
102
|
# Try to count the number of physical cores, not the number of
|
100
103
|
# logical ones. If the info is not available, fallback to the
|
101
104
|
# logical count
|
102
|
-
|
105
|
+
has_consistent_info =
|
106
|
+
(physical_ids.size == core_count.size) &&
|
107
|
+
(physical_ids.size == processor_ids.size)
|
108
|
+
if has_consistent_info
|
103
109
|
info = Array.new
|
104
110
|
while (id = physical_ids.shift)
|
105
111
|
info[id] = core_count.shift
|
@@ -112,18 +118,17 @@ def self.autodetect_processor_count
|
|
112
118
|
result = Open3.popen3("sysctl", "-n", "hw.ncpu") do |_, io, _|
|
113
119
|
io.read
|
114
120
|
end
|
115
|
-
|
116
|
-
@processor_count = Integer(result.chomp.strip)
|
117
|
-
end
|
121
|
+
@processor_count = Integer(result.chomp.strip) unless result.empty?
|
118
122
|
end
|
119
123
|
|
120
124
|
# The format of the cpuinfo file is ... let's say not very standardized.
|
121
125
|
# If the cpuinfo detection fails, inform the user and set it to 1
|
122
|
-
|
126
|
+
unless @processor_count
|
123
127
|
# Hug... What kind of system is it ?
|
124
128
|
Autobuild.message "INFO: cannot autodetect the number of CPUs on this sytem"
|
125
129
|
Autobuild.message "INFO: turning parallel builds off"
|
126
|
-
Autobuild.message "INFO: you can manually set the number of parallel build
|
130
|
+
Autobuild.message "INFO: you can manually set the number of parallel build "\
|
131
|
+
"processes to N"
|
127
132
|
Autobuild.message "INFO: (and therefore turn this message off)"
|
128
133
|
Autobuild.message "INFO: with"
|
129
134
|
Autobuild.message " Autobuild.parallel_build_level = N"
|
@@ -135,20 +140,24 @@ def self.autodetect_processor_count
|
|
135
140
|
|
136
141
|
def self.validate_displayed_error_line_count(lines)
|
137
142
|
if lines == 'ALL'
|
138
|
-
|
143
|
+
Float::INFINITY
|
139
144
|
elsif lines.to_i > 0
|
140
|
-
|
145
|
+
lines.to_i
|
146
|
+
else
|
147
|
+
raise ConfigException.new, 'Autobuild.displayed_error_line_count can only "\
|
148
|
+
"be a positive integer or \'ALL\''
|
141
149
|
end
|
142
|
-
raise ConfigException.new, 'Autobuild.displayed_error_line_count can only be a positive integer or \'ALL\''
|
143
150
|
end
|
144
151
|
end
|
145
152
|
|
146
|
-
|
147
|
-
|
148
|
-
class Failed < Exception
|
149
|
-
def retry?; @retry end
|
153
|
+
module Autobuild::Subprocess # rubocop:disable Style/ClassAndModuleChildren
|
154
|
+
class Failed < RuntimeError
|
150
155
|
attr_reader :status
|
151
156
|
|
157
|
+
def retry?
|
158
|
+
@retry
|
159
|
+
end
|
160
|
+
|
152
161
|
def initialize(status, do_retry)
|
153
162
|
@status = status
|
154
163
|
@retry = do_retry
|
@@ -206,7 +215,11 @@ def self.run(target, phase, *command)
|
|
206
215
|
STDOUT.sync = true
|
207
216
|
|
208
217
|
input_streams = []
|
209
|
-
options =
|
218
|
+
options = {
|
219
|
+
retry: false, encoding: 'BINARY',
|
220
|
+
env: ENV.to_hash, env_inherit: true
|
221
|
+
}
|
222
|
+
|
210
223
|
if command.last.kind_of?(Hash)
|
211
224
|
options = command.pop
|
212
225
|
options = Kernel.validate_options options,
|
@@ -216,19 +229,15 @@ def self.run(target, phase, *command)
|
|
216
229
|
env_inherit: true,
|
217
230
|
encoding: 'BINARY'
|
218
231
|
|
219
|
-
if options[:input]
|
220
|
-
|
221
|
-
end
|
222
|
-
if options[:input_streams]
|
223
|
-
input_streams += options[:input_streams]
|
224
|
-
end
|
232
|
+
input_streams << File.open(options[:input]) if options[:input]
|
233
|
+
input_streams.concat(options[:input_streams]) if options[:input_streams]
|
225
234
|
end
|
226
235
|
|
227
236
|
start_time = Time.now
|
228
237
|
|
229
238
|
# Filter nil and empty? in command
|
230
|
-
command.reject!
|
231
|
-
command.collect!
|
239
|
+
command.reject! { |o| o.nil? || (o.respond_to?(:empty?) && o.empty?) }
|
240
|
+
command.collect!(&:to_s)
|
232
241
|
|
233
242
|
if target.respond_to?(:name)
|
234
243
|
target_name = target.name
|
@@ -246,13 +255,15 @@ def self.run(target, phase, *command)
|
|
246
255
|
options[:working_directory] ||= target.working_directory
|
247
256
|
end
|
248
257
|
|
249
|
-
logname = File.join(logdir, "#{target_name.gsub(/[:]/,'_')}
|
250
|
-
|
258
|
+
logname = File.join(logdir, "#{target_name.gsub(/[:]/, '_')}-"\
|
259
|
+
"#{phase.to_s.gsub(/[:]/, '_')}.log")
|
260
|
+
unless File.directory?(File.dirname(logname))
|
251
261
|
FileUtils.mkdir_p File.dirname(logname)
|
252
262
|
end
|
253
263
|
|
254
264
|
if Autobuild.verbose
|
255
|
-
Autobuild.message "#{target_name}: running #{command.join(
|
265
|
+
Autobuild.message "#{target_name}: running #{command.join(' ')}\n"\
|
266
|
+
" (output goes to #{logname})"
|
256
267
|
end
|
257
268
|
|
258
269
|
open_flag = if Autobuild.keep_oldlogs then 'a'
|
@@ -267,32 +278,28 @@ def self.run(target, phase, *command)
|
|
267
278
|
env = options[:env].dup
|
268
279
|
if options[:env_inherit]
|
269
280
|
ENV.each do |k, v|
|
270
|
-
|
271
|
-
env[k] = v
|
272
|
-
end
|
281
|
+
env[k] = v unless env.key?(k)
|
273
282
|
end
|
274
283
|
end
|
275
284
|
|
276
285
|
status = File.open(logname, open_flag) do |logfile|
|
277
|
-
if Autobuild.keep_oldlogs
|
278
|
-
logfile.puts
|
279
|
-
end
|
286
|
+
logfile.puts if Autobuild.keep_oldlogs
|
280
287
|
logfile.puts
|
281
288
|
logfile.puts "#{Time.now}: running"
|
282
|
-
logfile.puts " #{command.join(
|
289
|
+
logfile.puts " #{command.join(' ')}"
|
283
290
|
logfile.puts "with environment:"
|
284
291
|
env.keys.sort.each do |key|
|
285
|
-
if value = env[key]
|
292
|
+
if (value = env[key])
|
286
293
|
logfile.puts " '#{key}'='#{value}'"
|
287
294
|
end
|
288
295
|
end
|
289
296
|
logfile.puts
|
290
297
|
logfile.puts "#{Time.now}: running"
|
291
|
-
logfile.puts " #{command.join(
|
298
|
+
logfile.puts " #{command.join(' ')}"
|
292
299
|
logfile.flush
|
293
300
|
logfile.sync = true
|
294
301
|
|
295
|
-
|
302
|
+
unless input_streams.empty?
|
296
303
|
pread, pwrite = IO.pipe # to feed subprocess stdin
|
297
304
|
end
|
298
305
|
|
@@ -304,22 +311,19 @@ def self.run(target, phase, *command)
|
|
304
311
|
|
305
312
|
if Autobuild.windows?
|
306
313
|
Dir.chdir(options[:working_directory]) do
|
307
|
-
|
308
|
-
raise Failed.new(
|
314
|
+
unless system(*command)
|
315
|
+
raise Failed.new($CHILD_STATUS.exitstatus, nil),
|
309
316
|
"'#{command.join(' ')}' returned status #{status.exitstatus}"
|
310
317
|
end
|
311
318
|
end
|
312
|
-
return
|
319
|
+
return # rubocop:disable Lint/NonLocalExitFromIterator
|
313
320
|
end
|
314
321
|
|
315
322
|
cwrite.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
316
323
|
|
317
324
|
pid = fork do
|
318
325
|
begin
|
319
|
-
|
320
|
-
Dir.chdir(options[:working_directory])
|
321
|
-
end
|
322
|
-
logfile.puts "in directory #{Dir.pwd}"
|
326
|
+
logfile.puts "in directory #{options[:working_directory] || Dir.pwd}"
|
323
327
|
|
324
328
|
cwrite.sync = true
|
325
329
|
if Autobuild.nice
|
@@ -330,19 +334,23 @@ def self.run(target, phase, *command)
|
|
330
334
|
$stderr.reopen(outwrite.dup)
|
331
335
|
$stdout.reopen(outwrite.dup)
|
332
336
|
|
333
|
-
|
337
|
+
unless input_streams.empty?
|
334
338
|
pwrite.close
|
335
339
|
$stdin.reopen(pread)
|
336
340
|
end
|
337
341
|
|
338
|
-
exec(env, *command,
|
342
|
+
exec(env, *command,
|
343
|
+
chdir: options[:working_directory] || Dir.pwd,
|
344
|
+
close_others: false)
|
339
345
|
rescue Errno::ENOENT
|
340
346
|
cwrite.write([CONTROL_COMMAND_NOT_FOUND].pack('I'))
|
341
347
|
exit(100)
|
342
348
|
rescue Interrupt
|
343
349
|
cwrite.write([CONTROL_INTERRUPT].pack('I'))
|
344
350
|
exit(100)
|
345
|
-
rescue ::Exception
|
351
|
+
rescue ::Exception => e
|
352
|
+
STDERR.puts e
|
353
|
+
STDERR.puts e.backtrace.join("\n ")
|
346
354
|
cwrite.write([CONTROL_UNEXPECTED].pack('I'))
|
347
355
|
exit(100)
|
348
356
|
end
|
@@ -351,12 +359,14 @@ def self.run(target, phase, *command)
|
|
351
359
|
readbuffer = StringIO.new
|
352
360
|
|
353
361
|
# Feed the input
|
354
|
-
|
362
|
+
unless input_streams.empty?
|
355
363
|
pread.close
|
356
364
|
begin
|
357
365
|
input_streams.each do |instream|
|
358
366
|
instream.each_line do |line|
|
359
|
-
|
367
|
+
while IO.select([outread], nil, nil, 0)
|
368
|
+
readbuffer.write(outread.readpartial(128))
|
369
|
+
end
|
360
370
|
pwrite.write(line)
|
361
371
|
end
|
362
372
|
end
|
@@ -385,15 +395,13 @@ def self.run(target, phase, *command)
|
|
385
395
|
end
|
386
396
|
|
387
397
|
transparent_prefix = "#{target_name}:#{phase}: "
|
388
|
-
if target_type
|
389
|
-
transparent_prefix = "#{target_type}:#{transparent_prefix}"
|
390
|
-
end
|
398
|
+
transparent_prefix = "#{target_type}:#{transparent_prefix}" if target_type
|
391
399
|
|
392
400
|
# If the caller asked for process output, provide it to him
|
393
401
|
# line-by-line.
|
394
402
|
outwrite.close
|
395
403
|
|
396
|
-
|
404
|
+
unless input_streams.empty?
|
397
405
|
readbuffer.write(outread.read)
|
398
406
|
readbuffer.seek(0)
|
399
407
|
outread.close
|
@@ -428,6 +436,7 @@ def self.run(target, phase, *command)
|
|
428
436
|
if status.termsig == 2 # SIGINT == 2
|
429
437
|
raise Interrupt, "subcommand #{command.join(' ')} interrupted"
|
430
438
|
end
|
439
|
+
|
431
440
|
if status.termsig
|
432
441
|
raise Failed.new(status.exitstatus, nil),
|
433
442
|
"'#{command.join(' ')}' terminated by signal #{status.termsig}"
|
@@ -441,21 +450,19 @@ def self.run(target, phase, *command)
|
|
441
450
|
Autobuild.add_stat(target, phase, duration)
|
442
451
|
FileUtils.mkdir_p(Autobuild.logdir)
|
443
452
|
File.open(File.join(Autobuild.logdir, "stats.log"), 'a') do |io|
|
444
|
-
|
453
|
+
formatted_msec = format('%.03i', start_time.tv_usec / 1000)
|
454
|
+
formatted_time = "#{start_time.strftime('%F %H:%M:%S')}.#{formatted_msec}"
|
445
455
|
io.puts "#{formatted_time} #{target_name} #{phase} #{duration}"
|
446
456
|
end
|
447
|
-
if target.respond_to?(:add_stat)
|
448
|
-
target.add_stat(phase, duration)
|
449
|
-
end
|
457
|
+
target.add_stat(phase, duration) if target.respond_to?(:add_stat)
|
450
458
|
subcommand_output
|
451
|
-
|
452
459
|
rescue Failed => e
|
453
|
-
error = Autobuild::SubcommandFailed.new(target, command.join(" "),
|
460
|
+
error = Autobuild::SubcommandFailed.new(target, command.join(" "),
|
461
|
+
logname, e.status, subcommand_output)
|
454
462
|
error.retry = if e.retry?.nil? then options[:retry]
|
455
463
|
else e.retry?
|
456
464
|
end
|
457
465
|
error.phase = phase
|
458
466
|
raise error, e.message
|
459
467
|
end
|
460
|
-
|
461
468
|
end
|