yugui-chkbuild 0.1.2

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 (66) hide show
  1. data/README.ja.rd +191 -0
  2. data/Rakefile +56 -0
  3. data/VERSION +1 -0
  4. data/bin/last-build +28 -0
  5. data/bin/start-build +37 -0
  6. data/chkbuild.gemspec +107 -0
  7. data/core_ext/io.rb +17 -0
  8. data/core_ext/string.rb +10 -0
  9. data/lib/chkbuild.rb +45 -0
  10. data/lib/chkbuild/build.rb +718 -0
  11. data/lib/chkbuild/lock.rb +57 -0
  12. data/lib/chkbuild/logfile.rb +230 -0
  13. data/lib/chkbuild/main.rb +138 -0
  14. data/lib/chkbuild/options.rb +62 -0
  15. data/lib/chkbuild/scm/cvs.rb +132 -0
  16. data/lib/chkbuild/scm/git.rb +223 -0
  17. data/lib/chkbuild/scm/svn.rb +215 -0
  18. data/lib/chkbuild/scm/xforge.rb +33 -0
  19. data/lib/chkbuild/target.rb +180 -0
  20. data/lib/chkbuild/targets/gcc.rb +94 -0
  21. data/lib/chkbuild/targets/ruby.rb +456 -0
  22. data/lib/chkbuild/title.rb +107 -0
  23. data/lib/chkbuild/upload.rb +66 -0
  24. data/lib/misc/escape.rb +535 -0
  25. data/lib/misc/gdb.rb +74 -0
  26. data/lib/misc/timeoutcom.rb +174 -0
  27. data/lib/misc/udiff.rb +244 -0
  28. data/lib/misc/util.rb +232 -0
  29. data/sample/build-autoconf-ruby +69 -0
  30. data/sample/build-gcc-ruby +43 -0
  31. data/sample/build-ruby +37 -0
  32. data/sample/build-ruby2 +36 -0
  33. data/sample/build-svn +55 -0
  34. data/sample/build-yarv +35 -0
  35. data/sample/test-apr +12 -0
  36. data/sample/test-catcherr +23 -0
  37. data/sample/test-combfail +21 -0
  38. data/sample/test-core +14 -0
  39. data/sample/test-core2 +19 -0
  40. data/sample/test-date +9 -0
  41. data/sample/test-dep +17 -0
  42. data/sample/test-depver +14 -0
  43. data/sample/test-echo +9 -0
  44. data/sample/test-env +9 -0
  45. data/sample/test-error +9 -0
  46. data/sample/test-fail +18 -0
  47. data/sample/test-fmesg +16 -0
  48. data/sample/test-gcc-v +15 -0
  49. data/sample/test-git +11 -0
  50. data/sample/test-leave-proc +9 -0
  51. data/sample/test-limit +9 -0
  52. data/sample/test-make +9 -0
  53. data/sample/test-neterr +16 -0
  54. data/sample/test-savannah +14 -0
  55. data/sample/test-sleep +9 -0
  56. data/sample/test-timeout +9 -0
  57. data/sample/test-timeout2 +10 -0
  58. data/sample/test-timeout3 +9 -0
  59. data/sample/test-upload +13 -0
  60. data/sample/test-warn +13 -0
  61. data/setup/upload-rsync-ssh +572 -0
  62. data/test/misc/test-escape.rb +17 -0
  63. data/test/misc/test-logfile.rb +108 -0
  64. data/test/misc/test-timeoutcom.rb +23 -0
  65. data/test/test_helper.rb +9 -0
  66. metadata +123 -0
@@ -0,0 +1,74 @@
1
+ # Copyright (C) 2005,2006,2009 Tanaka Akira <akr@fsij.org>
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are met:
5
+ #
6
+ # 1. Redistributions of source code must retain the above copyright notice, this
7
+ # list of conditions and the following disclaimer.
8
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
9
+ # this list of conditions and the following disclaimer in the documentation
10
+ # and/or other materials provided with the distribution.
11
+ # 3. The name of the author may not be used to endorse or promote products
12
+ # derived from this software without specific prior written permission.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
15
+ # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16
+ # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
17
+ # EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
19
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
22
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
23
+ # OF SUCH DAMAGE.
24
+
25
+ require 'find'
26
+ require 'tempfile'
27
+
28
+ require 'escape'
29
+
30
+ module GDB
31
+ module_function
32
+
33
+ def check_core(dir)
34
+ binaries = {}
35
+ core_info = []
36
+ Find.find(dir.to_s) {|f|
37
+ stat = File.stat(f)
38
+ basename = File.basename(f)
39
+ binaries[basename] = f if stat.file? && stat.executable?
40
+ next if /\bcore\b/ !~ basename
41
+ next if /\.chkbuild\.\d+\z/ =~ basename
42
+ guess = `file #{f} 2>&1`
43
+ next if /\bcore\b.*from '(.*?)'/ !~ guess.sub(/\A.*?:/, '')
44
+ core_info << [f, $1]
45
+ }
46
+ gdb_command = nil
47
+ core_info.each {|core_path, binary|
48
+ next unless binary_path = binaries[binary]
49
+ core_path = rename_core(core_path)
50
+ unless gdb_command
51
+ gdb_command = Tempfile.new("gdb-bt")
52
+ gdb_command.puts "bt"
53
+ gdb_command.close
54
+ end
55
+ puts
56
+ puts "binary: #{binary_path}"
57
+ puts "core: #{core_path}"
58
+ command = %W[gdb -batch -n -x #{gdb_command.path} #{binary_path} #{core_path}]
59
+ gdb_output = `#{Escape.shell_command command}`
60
+ puts gdb_output
61
+ puts "gdb status: #{$?}"
62
+ }
63
+ end
64
+
65
+ def rename_core(core_path)
66
+ suffix = ".chkbuild."
67
+ n = 1
68
+ while File.exist?(new_path = "#{core_path}.chkbuild.#{n}")
69
+ n += 1
70
+ end
71
+ File.rename(core_path, new_path)
72
+ new_path
73
+ end
74
+ end
@@ -0,0 +1,174 @@
1
+ # Copyright (C) 2005,2006,2007,2008,2009 Tanaka Akira <akr@fsij.org>
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are met:
5
+ #
6
+ # 1. Redistributions of source code must retain the above copyright notice, this
7
+ # list of conditions and the following disclaimer.
8
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
9
+ # this list of conditions and the following disclaimer in the documentation
10
+ # and/or other materials provided with the distribution.
11
+ # 3. The name of the author may not be used to endorse or promote products
12
+ # derived from this software without specific prior written permission.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
15
+ # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16
+ # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
17
+ # EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
19
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
22
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
23
+ # OF SUCH DAMAGE.
24
+
25
+ class CommandTimeout < StandardError
26
+ end
27
+
28
+ module TimeoutCommand
29
+
30
+ module_function
31
+
32
+ def parse_timespan(arg)
33
+ case arg
34
+ when Integer, Float
35
+ timeout = arg
36
+ when Time
37
+ timeout = arg - Time.now
38
+ when /\A\d+(\.\d+)?(?:s|sec)?\z/
39
+ timeout = $&.to_f
40
+ when /\A\d+(\.\d+)?(?:m|min)\z/
41
+ timeout = $&.to_f * 60
42
+ when /\A\d+(\.\d+)?(?:h|hour)\z/
43
+ timeout = $&.to_f * 60 * 60
44
+ when /\A\d+(\.\d+)?(?:d|day)\z/
45
+ timeout = $&.to_f * 60 * 60 * 24
46
+ else
47
+ raise ArgumentError, "invalid time span: #{arg.inspect}"
48
+ end
49
+ timeout
50
+ end
51
+
52
+ def kill_processgroup(pgid, msgout)
53
+ begin
54
+ Process.kill('INT', -pgid)
55
+ msgout.puts "timeout: INT signal sent." if msgout
56
+ signals = ['INT', 'TERM', 'TERM', 'KILL']
57
+ signals.each {|sig|
58
+ Process.kill(0, -pgid); sleep 0.1
59
+ Process.kill(0, -pgid); sleep 0.2
60
+ Process.kill(0, -pgid); sleep 0.3
61
+ Process.kill(0, -pgid); sleep 0.4
62
+ Process.kill(0, -pgid)
63
+ 4.times {
64
+ sleep 1
65
+ Process.kill(0, -pgid)
66
+ }
67
+ Process.kill(sig, -pgid)
68
+ msgout.puts "timeout: #{sig} signal sent." if msgout
69
+ }
70
+ rescue Errno::ESRCH # no process i.e. success to kill
71
+ end
72
+ end
73
+
74
+ def process_alive?(pid)
75
+ begin
76
+ Process.kill(0, pid)
77
+ rescue Errno::ESRCH # no process
78
+ return false
79
+ end
80
+ return true
81
+ end
82
+
83
+ def processgroup_alive?(pgid)
84
+ process_alive?(-pgid)
85
+ end
86
+
87
+ def last_output_time
88
+ last_output_time = [STDOUT, STDERR].map {|f|
89
+ s = f.stat
90
+ if s.file?
91
+ s.mtime
92
+ else
93
+ nil
94
+ end
95
+ }.compact
96
+ if last_output_time.empty?
97
+ nil
98
+ else
99
+ last_output_time.max
100
+ end
101
+ end
102
+
103
+ def timeout_command(command_timeout, msgout=STDERR, opts={})
104
+ command_timeout = parse_timespan(command_timeout)
105
+ output_interval_timeout = nil
106
+ if opts[:output_interval_timeout]
107
+ output_interval_timeout = parse_timespan(opts[:output_interval_timeout])
108
+ end
109
+ if command_timeout < 0
110
+ raise CommandTimeout, 'no time to run a command'
111
+ end
112
+ pid = fork {
113
+ Process.setpgid($$, $$)
114
+ yield
115
+ }
116
+ begin
117
+ Process.setpgid(pid, pid)
118
+ rescue Errno::EACCES # already execed.
119
+ rescue Errno::ESRCH # already exited. (setpgid for a zombie fails on OpenBSD)
120
+ end
121
+ wait_thread = Thread.new {
122
+ Process.wait2(pid)[1]
123
+ }
124
+ begin
125
+ start_time = Time.now
126
+ limit_time = start_time + command_timeout
127
+ command_status = nil
128
+ while true
129
+ join_timeout = limit_time - Time.now
130
+ if join_timeout < 0
131
+ timeout_reason = "command execution time exceeds #{command_timeout} seconds."
132
+ break
133
+ end
134
+ if output_interval_timeout and
135
+ t = last_output_time and
136
+ (tmp_join_timeout = t + output_interval_timeout - Time.now) < join_timeout
137
+ join_timeout = tmp_join_timeout
138
+ if join_timeout < 0
139
+ timeout_reason = "output interval exceeds #{output_interval_timeout} seconds."
140
+ break
141
+ end
142
+ end
143
+ if wait_thread.join(join_timeout)
144
+ command_status = wait_thread.value
145
+ break
146
+ end
147
+ end
148
+ if command_status
149
+ return command_status
150
+ else
151
+ msgout.puts "timeout: #{timeout_reason}" if msgout
152
+ begin
153
+ Process.kill(0, -pid)
154
+ msgout.puts "timeout: the process group #{pid} is alive." if msgout
155
+ kill_processgroup(pid, msgout)
156
+ rescue Errno::ESRCH # no process
157
+ end
158
+ raise CommandTimeout, timeout_reason
159
+ end
160
+ rescue Interrupt
161
+ Process.kill("INT", -pid)
162
+ raise
163
+ rescue SignalException
164
+ Process.kill($!.message, -pid)
165
+ raise
166
+ ensure
167
+ if processgroup_alive?(pid)
168
+ msgout.puts "some descendant process in process group #{pid} remain." if msgout
169
+ kill_processgroup(pid, msgout)
170
+ end
171
+ end
172
+ end
173
+ end
174
+
@@ -0,0 +1,244 @@
1
+ # Copyright (C) 2005,2006,2007,2008 Tanaka Akira <akr@fsij.org>
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are met:
5
+ #
6
+ # 1. Redistributions of source code must retain the above copyright notice, this
7
+ # list of conditions and the following disclaimer.
8
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
9
+ # this list of conditions and the following disclaimer in the documentation
10
+ # and/or other materials provided with the distribution.
11
+ # 3. The name of the author may not be used to endorse or promote products
12
+ # derived from this software without specific prior written permission.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
15
+ # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16
+ # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
17
+ # EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
19
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
22
+ # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
23
+ # OF SUCH DAMAGE.
24
+
25
+ require 'escape'
26
+ require 'tempfile'
27
+
28
+ class UDiff
29
+ def UDiff.diff(path1, path2, out, header="--- #{path1}\n+++ #{path2}\n")
30
+ UDiff.new(path1, path2, out, header).diff
31
+ end
32
+
33
+ def initialize(path1, path2, out, header)
34
+ @path1 = path1
35
+ @path2 = path2
36
+ @out = out
37
+ @header = header
38
+ @context = 3
39
+ @beginning = true
40
+ @l1 = 0
41
+ @l2 = 0
42
+ @hunk_beg = [@l1, @l2]
43
+ @hunk = []
44
+ @lines_hash = {}
45
+ @lines_ary = []
46
+ end
47
+
48
+ def puts_line(line)
49
+ @hunk << line
50
+ if /\n\z/ !~ line
51
+ @hunk << "\n\\n"
52
+ end
53
+ @beginning = false
54
+ end
55
+
56
+ def puts_del_line(line)
57
+ line = /\A_/ =~ line ? $' : @lines_ary[line.to_i]
58
+ puts_line "-#{line}"
59
+ end
60
+
61
+ def puts_add_line(line)
62
+ line = /\A_/ =~ line ? $' : @lines_ary[line.to_i]
63
+ puts_line "+#{line}"
64
+ end
65
+
66
+ def puts_common_line(line)
67
+ line = /\A_/ =~ line ? $' : @lines_ary[line.to_i]
68
+ puts_line " #{line}"
69
+ end
70
+
71
+ def encdump(str)
72
+ d = str.dump
73
+ if str.respond_to? :encoding
74
+ "#{d}.force_encoding(#{str.encoding.name.dump})"
75
+ else
76
+ d
77
+ end
78
+ end
79
+
80
+ def gets_common_line(f1, f2)
81
+ v1 = f1.gets
82
+ v2 = f2.gets
83
+ if v1 != v2
84
+ raise "[bug] diff error: #{encdump v1} != #{encdump v2}"
85
+ end
86
+ if v1
87
+ @l1 += 1
88
+ @l2 += 1
89
+ end
90
+ return v1
91
+ end
92
+
93
+ def copy_common_part(f1, f2, n)
94
+ n.times {|i|
95
+ v = gets_common_line(f1, f2)
96
+ raise "[bug] diff error: unexpected EOF" unless v
97
+ puts_common_line(v)
98
+ }
99
+ end
100
+
101
+ def skip_common_part(f1, f2, n)
102
+ n.times {|i|
103
+ v = gets_common_line(f1, f2)
104
+ raise "[bug] diff error: unexpected EOF" unless v
105
+ }
106
+ end
107
+
108
+ def output_hunk
109
+ if @header
110
+ @out.print @header
111
+ @header = nil
112
+ end
113
+ l1_beg, l2_beg = @hunk_beg
114
+ @out.print "@@ -#{l1_beg+1},#{@l1-l1_beg} +#{l2_beg+1},#{@l2-l2_beg} @@\n"
115
+ @hunk.each {|s|
116
+ @out.print s
117
+ }
118
+ end
119
+
120
+ def output_common_part(f1, f2, common_num)
121
+ if @beginning
122
+ if common_num <= @context
123
+ copy_common_part(f1, f2, common_num)
124
+ else
125
+ skip_common_part(f1, f2, common_num-@context)
126
+ copy_common_part(f1, f2, @context)
127
+ end
128
+ elsif common_num <= @context * 2
129
+ copy_common_part(f1, f2, common_num)
130
+ else
131
+ copy_common_part(f1, f2, @context)
132
+ output_hunk
133
+ skip_common_part(f1, f2, common_num-@context*2)
134
+ @hunk_beg = [@l1, @l2]
135
+ @hunk = []
136
+ copy_common_part(f1, f2, @context)
137
+ end
138
+ end
139
+
140
+ def output_common_tail(f1, f2)
141
+ return if @beginning
142
+ @context.times {
143
+ v = gets_common_line(f1, f2)
144
+ break unless v
145
+ puts_common_line(v)
146
+ }
147
+ output_hunk
148
+ end
149
+
150
+ def process_commands(f1, f2, d)
151
+ has_diff = false
152
+ l1 = 0
153
+ while com = d.gets
154
+ case com
155
+ when /\Ad(\d+) (\d+)/
156
+ line = $1.to_i
157
+ num = $2.to_i
158
+ output_common_part(f1, f2, line-l1-1)
159
+ num.times {
160
+ v = f1.gets
161
+ @l1 += 1
162
+ puts_del_line(v)
163
+ has_diff = true
164
+ }
165
+ l1 = line + num - 1
166
+ when /\Aa(\d+) (\d+)/
167
+ line = $1.to_i
168
+ num = $2.to_i
169
+ common_num = line-l1
170
+ output_common_part(f1, f2, line-l1)
171
+ l1 = line
172
+ num.times {
173
+ v1 = d.gets
174
+ v2 = f2.gets
175
+ if v1 != v2
176
+ raise "[bug] diff error: #{encdump v1} != #{encdump v2}"
177
+ end
178
+ @l2 += 1
179
+ v = v1
180
+ puts_add_line(v)
181
+ has_diff = true
182
+ }
183
+ else
184
+ raise "[bug] unexpected diff line: #{com.inspect}"
185
+ end
186
+ end
187
+ has_diff
188
+ end
189
+
190
+ def run_diff(path1, path2)
191
+ has_diff = false
192
+ open(path1) {|f1|
193
+ f1.set_encoding "ascii-8bit" if f1.respond_to? :set_encoding
194
+ open(path2) {|f2|
195
+ f2.set_encoding "ascii-8bit" if f2.respond_to? :set_encoding
196
+ command = Escape.shell_command(%W[diff -n #{path1} #{path2}]).to_s
197
+ command = "LC_ALL='C' LANG='C' #{command}"
198
+ IO.popen(command) {|d|
199
+ d.set_encoding "ascii-8bit" if d.respond_to? :set_encoding
200
+ has_diff = process_commands(f1, f2, d)
201
+ }
202
+ output_common_tail(f1, f2)
203
+ }
204
+ }
205
+ has_diff
206
+ end
207
+
208
+ SAFE_LINE = /\A[\t -~]*\n\z/
209
+ def diff
210
+ t1 = Tempfile.new("udiff")
211
+ File.foreach(@path1) {|l|
212
+ if SAFE_LINE =~ l
213
+ l = "_" + l
214
+ else
215
+ if !@lines_hash[l]
216
+ @lines_ary[@lines_hash.size] = l
217
+ @lines_hash[l] = @lines_hash.size
218
+ end
219
+ l = @lines_hash[l].to_s + "\n"
220
+ end
221
+ t1.puts l
222
+ }
223
+ t2 = Tempfile.new("udiff")
224
+ File.foreach(@path2) {|l|
225
+ if SAFE_LINE =~ l
226
+ l = "_" + l
227
+ else
228
+ if !@lines_hash[l]
229
+ @lines_ary[@lines_hash.size] = l
230
+ @lines_hash[l] = @lines_hash.size
231
+ end
232
+ l = @lines_hash[l].to_s + "\n"
233
+ end
234
+ t2.puts l
235
+ }
236
+ t1.close
237
+ t2.close
238
+ run_diff(t1.path, t2.path)
239
+ end
240
+ end
241
+
242
+ if $0 == __FILE__
243
+ UDiff.diff(ARGV[0], ARGV[1], STDOUT)
244
+ end