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.
- data/README.ja.rd +191 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/bin/last-build +28 -0
- data/bin/start-build +37 -0
- data/chkbuild.gemspec +107 -0
- data/core_ext/io.rb +17 -0
- data/core_ext/string.rb +10 -0
- data/lib/chkbuild.rb +45 -0
- data/lib/chkbuild/build.rb +718 -0
- data/lib/chkbuild/lock.rb +57 -0
- data/lib/chkbuild/logfile.rb +230 -0
- data/lib/chkbuild/main.rb +138 -0
- data/lib/chkbuild/options.rb +62 -0
- data/lib/chkbuild/scm/cvs.rb +132 -0
- data/lib/chkbuild/scm/git.rb +223 -0
- data/lib/chkbuild/scm/svn.rb +215 -0
- data/lib/chkbuild/scm/xforge.rb +33 -0
- data/lib/chkbuild/target.rb +180 -0
- data/lib/chkbuild/targets/gcc.rb +94 -0
- data/lib/chkbuild/targets/ruby.rb +456 -0
- data/lib/chkbuild/title.rb +107 -0
- data/lib/chkbuild/upload.rb +66 -0
- data/lib/misc/escape.rb +535 -0
- data/lib/misc/gdb.rb +74 -0
- data/lib/misc/timeoutcom.rb +174 -0
- data/lib/misc/udiff.rb +244 -0
- data/lib/misc/util.rb +232 -0
- data/sample/build-autoconf-ruby +69 -0
- data/sample/build-gcc-ruby +43 -0
- data/sample/build-ruby +37 -0
- data/sample/build-ruby2 +36 -0
- data/sample/build-svn +55 -0
- data/sample/build-yarv +35 -0
- data/sample/test-apr +12 -0
- data/sample/test-catcherr +23 -0
- data/sample/test-combfail +21 -0
- data/sample/test-core +14 -0
- data/sample/test-core2 +19 -0
- data/sample/test-date +9 -0
- data/sample/test-dep +17 -0
- data/sample/test-depver +14 -0
- data/sample/test-echo +9 -0
- data/sample/test-env +9 -0
- data/sample/test-error +9 -0
- data/sample/test-fail +18 -0
- data/sample/test-fmesg +16 -0
- data/sample/test-gcc-v +15 -0
- data/sample/test-git +11 -0
- data/sample/test-leave-proc +9 -0
- data/sample/test-limit +9 -0
- data/sample/test-make +9 -0
- data/sample/test-neterr +16 -0
- data/sample/test-savannah +14 -0
- data/sample/test-sleep +9 -0
- data/sample/test-timeout +9 -0
- data/sample/test-timeout2 +10 -0
- data/sample/test-timeout3 +9 -0
- data/sample/test-upload +13 -0
- data/sample/test-warn +13 -0
- data/setup/upload-rsync-ssh +572 -0
- data/test/misc/test-escape.rb +17 -0
- data/test/misc/test-logfile.rb +108 -0
- data/test/misc/test-timeoutcom.rb +23 -0
- data/test/test_helper.rb +9 -0
- metadata +123 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
# Copyright (C) 2006,2007,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 "uri"
|
26
|
+
|
27
|
+
class ChkBuild::Build
|
28
|
+
def cvs(cvsroot, mod, branch, opts={})
|
29
|
+
network_access {
|
30
|
+
cvs_internal(cvsroot, mod, branch, opts)
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def cvs_internal(cvsroot, mod, branch, opts={})
|
35
|
+
opts = opts.dup
|
36
|
+
opts[:section] ||= 'cvs'
|
37
|
+
working_dir = opts.fetch(:working_dir, mod)
|
38
|
+
if !File.exist? "#{ENV['HOME']}/.cvspass"
|
39
|
+
opts['ENV:CVS_PASSFILE'] = '/dev/null' # avoid warning
|
40
|
+
end
|
41
|
+
if File.directory?(working_dir)
|
42
|
+
Dir.chdir(working_dir) {
|
43
|
+
h1 = cvs_revisions
|
44
|
+
self.run("cvs", "-f", "-z3", "-Q", "update", "-kb", "-dP", opts)
|
45
|
+
h2 = cvs_revisions
|
46
|
+
cvs_print_revisions(h1, h2, opts[:viewvc]||opts[:viewcvs]||opts[:cvsweb])
|
47
|
+
}
|
48
|
+
else
|
49
|
+
h1 = nil
|
50
|
+
if File.identical?(@build_dir, '.') &&
|
51
|
+
!(ts = build_time_sequence - [@start_time]).empty? &&
|
52
|
+
File.directory?(old_working_dir = "#{@target_dir}/#{ts.last}/#{working_dir}")
|
53
|
+
Dir.chdir(old_working_dir) {
|
54
|
+
h1 = cvs_revisions
|
55
|
+
}
|
56
|
+
end
|
57
|
+
if branch
|
58
|
+
self.run("cvs", "-f", "-z3", "-Qd", cvsroot, "co", "-kb", "-d", working_dir, "-Pr", branch, mod, opts)
|
59
|
+
else
|
60
|
+
self.run("cvs", "-f", "-z3", "-Qd", cvsroot, "co", "-kb", "-d", working_dir, "-P", mod, opts)
|
61
|
+
end
|
62
|
+
Dir.chdir(working_dir) {
|
63
|
+
h2 = cvs_revisions
|
64
|
+
cvs_print_revisions(h1, h2, opts[:viewvc]||opts[:viewcvs]||opts[:cvsweb])
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def cvs_revisions
|
70
|
+
h = {}
|
71
|
+
Dir.glob("**/CVS").each {|cvs_dir|
|
72
|
+
cvsroot = IO.read("#{cvs_dir}/Root").chomp
|
73
|
+
repository = IO.read("#{cvs_dir}/Repository").chomp
|
74
|
+
ds = cvs_dir.split(%r{/})[0...-1]
|
75
|
+
IO.foreach("#{cvs_dir}/Entries") {|line|
|
76
|
+
h[[ds, $1]] = [cvsroot, repository, $2] if %r{^/([^/]+)/([^/]*)/} =~ line
|
77
|
+
}
|
78
|
+
}
|
79
|
+
h
|
80
|
+
end
|
81
|
+
|
82
|
+
def cvs_uri(viewcvs, repository, filename, r1, r2)
|
83
|
+
uri = URI.parse(viewcvs)
|
84
|
+
path = uri.path.dup
|
85
|
+
path << "/" << Escape.uri_path(repository).to_s if repository != '.'
|
86
|
+
path << "/" << Escape.uri_path(filename).to_s
|
87
|
+
uri.path = path
|
88
|
+
query = (uri.query || '').split(/[;&]/)
|
89
|
+
if r1 == 'none'
|
90
|
+
query << "rev=#{r2}"
|
91
|
+
elsif r2 == 'none'
|
92
|
+
query << "rev=#{r1}"
|
93
|
+
else
|
94
|
+
query << "r1=#{r1}" << "r2=#{r2}"
|
95
|
+
end
|
96
|
+
uri.query = query.join(';')
|
97
|
+
uri.to_s
|
98
|
+
end
|
99
|
+
|
100
|
+
def cvs_print_changes(h1, h2, viewcvs=nil)
|
101
|
+
(h1.keys | h2.keys).sort.each {|k|
|
102
|
+
f = k.flatten.join('/')
|
103
|
+
cvsroot1, repository1, r1 = h1[k] || [nil, nil, 'none']
|
104
|
+
cvsroot2, repository2, r2 = h2[k] || [nil, nil, 'none']
|
105
|
+
if r1 != r2
|
106
|
+
if r1 == 'none'
|
107
|
+
line = "ADD"
|
108
|
+
elsif r2 == 'none'
|
109
|
+
line = "DEL"
|
110
|
+
else
|
111
|
+
line = "CHG"
|
112
|
+
end
|
113
|
+
line << " #{f}\t#{r1}->#{r2}"
|
114
|
+
if viewcvs
|
115
|
+
line << "\t" << cvs_uri(viewcvs, repository1 || repository2, k[1], r1, r2)
|
116
|
+
end
|
117
|
+
puts line
|
118
|
+
end
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
def cvs_print_revisions(h1, h2, viewcvs=nil)
|
123
|
+
cvs_print_changes(h1, h2, viewcvs) if h1
|
124
|
+
puts 'revisions:'
|
125
|
+
h2.keys.sort.each {|k|
|
126
|
+
f = k.flatten.join('/')
|
127
|
+
cvsroot2, repository2, r2 = h2[k] || [nil, nil, 'none']
|
128
|
+
digest = sha256_digest_file(f)
|
129
|
+
puts "#{f}\t#{r2}\t#{digest}"
|
130
|
+
}
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
# Copyright (C) 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
|
+
require 'fileutils'
|
26
|
+
require "uri"
|
27
|
+
|
28
|
+
require "pp"
|
29
|
+
|
30
|
+
module ChkBuild; end # for testing
|
31
|
+
|
32
|
+
class ChkBuild::Build
|
33
|
+
def git_with_file(basename)
|
34
|
+
n = 1
|
35
|
+
until !File.exist?(name = "#{self.build_dir}/#{basename}#{n}")
|
36
|
+
n += 1
|
37
|
+
end
|
38
|
+
yield name
|
39
|
+
end
|
40
|
+
|
41
|
+
def git_logfile(opts)
|
42
|
+
git_with_file("git.log.") {|errfile|
|
43
|
+
opts2 = opts.dup
|
44
|
+
opts2[:stderr] = errfile
|
45
|
+
begin
|
46
|
+
yield opts2
|
47
|
+
ensure
|
48
|
+
if File.exist?(errfile)
|
49
|
+
errcontent = File.read(errfile)
|
50
|
+
errcontent.gsub!(/^.*[\r\e].*\n/, "")
|
51
|
+
puts errcontent if !errcontent.empty?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def git(cloneurl, working_dir, opts={})
|
58
|
+
network_access {
|
59
|
+
git_internal(cloneurl, working_dir, opts)
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def git_internal(cloneurl, working_dir, opts={})
|
64
|
+
urigen = nil
|
65
|
+
opts = opts.dup
|
66
|
+
opts[:section] ||= 'git'
|
67
|
+
if opts[:github]
|
68
|
+
urigen = GitHub.new(*opts[:github])
|
69
|
+
end
|
70
|
+
if shared_dir = opts[:shared_gitdir]
|
71
|
+
opts_shared = opts.dup
|
72
|
+
opts_shared[:section] += "(shared)"
|
73
|
+
Dir.chdir(shared_dir) {
|
74
|
+
if File.directory?(working_dir) && File.exist?("#{working_dir}/.git")
|
75
|
+
Dir.chdir(working_dir) {
|
76
|
+
git_logfile(opts_shared) {|opts2|
|
77
|
+
self.run("git", "pull", opts2)
|
78
|
+
}
|
79
|
+
}
|
80
|
+
else
|
81
|
+
FileUtils.rm_rf(working_dir) if File.exist?(working_dir)
|
82
|
+
pdir = File.dirname(working_dir)
|
83
|
+
FileUtils.mkdir_p(pdir) if !File.directory?(pdir)
|
84
|
+
git_logfile(opts_shared) {|opts2|
|
85
|
+
self.run "git", "clone", "-q", cloneurl, working_dir, opts2
|
86
|
+
}
|
87
|
+
end
|
88
|
+
cloneurl = "#{shared_dir}/#{working_dir}"
|
89
|
+
}
|
90
|
+
end
|
91
|
+
if File.exist?(working_dir) && File.exist?("#{working_dir}/.git")
|
92
|
+
Dir.chdir(working_dir) {
|
93
|
+
old_head = git_head_commit
|
94
|
+
git_logfile(opts) {|opts2|
|
95
|
+
self.run "git", "pull", opts2
|
96
|
+
}
|
97
|
+
logs = git_oneline_logs(old_head)
|
98
|
+
git_print_logs(old_head, logs, urigen)
|
99
|
+
}
|
100
|
+
else
|
101
|
+
FileUtils.rm_rf(working_dir) if File.exist?(working_dir)
|
102
|
+
pdir = File.dirname(working_dir)
|
103
|
+
FileUtils.mkdir_p(pdir) if !File.directory?(pdir)
|
104
|
+
old_head = nil
|
105
|
+
if File.identical?(self.build_dir, '.') &&
|
106
|
+
!(ts = self.build_time_sequence - [self.start_time]).empty? &&
|
107
|
+
File.directory?(old_working_dir = self.target_dir + ts.last + working_dir)
|
108
|
+
Dir.chdir(old_working_dir) {
|
109
|
+
old_head = git_head_commit
|
110
|
+
}
|
111
|
+
end
|
112
|
+
git_logfile(opts) {|opts2|
|
113
|
+
self.run "git", "clone", "-q", cloneurl, working_dir, opts2
|
114
|
+
}
|
115
|
+
Dir.chdir(working_dir) {
|
116
|
+
logs = git_oneline_logs(old_head)
|
117
|
+
git_print_logs(old_head, logs, urigen)
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def github(user, project, working_dir, opts={})
|
123
|
+
opts = opts.dup
|
124
|
+
opts[:github] = [user, project]
|
125
|
+
git("git://github.com/#{user}/#{project}.git", working_dir, opts)
|
126
|
+
end
|
127
|
+
|
128
|
+
def git_oneline_logs(old_head=nil)
|
129
|
+
result = []
|
130
|
+
if old_head
|
131
|
+
command = "git log --pretty=oneline #{old_head}..HEAD"
|
132
|
+
else
|
133
|
+
command = "git log --pretty=oneline --max-count=1"
|
134
|
+
end
|
135
|
+
IO.popen(command) {|f|
|
136
|
+
f.each_line {|line|
|
137
|
+
# <sha1><sp><title line>
|
138
|
+
if /\A([0-9a-fA-F]+)\s+(.*)/ =~ line
|
139
|
+
result << [$1, $2]
|
140
|
+
end
|
141
|
+
}
|
142
|
+
}
|
143
|
+
result
|
144
|
+
end
|
145
|
+
|
146
|
+
def git_head_commit
|
147
|
+
IO.popen("git rev-list --max-count=1 HEAD") {|f|
|
148
|
+
# <sha1><LF>
|
149
|
+
# 4db0223676a371da8c4247d9a853529ef50a3b01
|
150
|
+
f.read.chomp
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
def git_revisions
|
155
|
+
h = IO.popen("git ls-tree -z -r HEAD") {|f|
|
156
|
+
git_parse_status(f)
|
157
|
+
}
|
158
|
+
IO.popen("git rev-list --max-count=1 HEAD") {|f|
|
159
|
+
# <sha1><LF>
|
160
|
+
# 4db0223676a371da8c4247d9a853529ef50a3b01
|
161
|
+
commit_hash = f.read.chomp
|
162
|
+
h[nil] = commit_hash
|
163
|
+
}
|
164
|
+
h
|
165
|
+
end
|
166
|
+
|
167
|
+
def git_parse_status(f)
|
168
|
+
h = {}
|
169
|
+
f.each_line("\0") {|line|
|
170
|
+
# <mode> SP <type> SP <object> TAB <file>\0
|
171
|
+
# 100644 blob 9518934185ea26856cf1bcdf75f7cc51fcd82534 core/array/allocate_spec.rb
|
172
|
+
if /\A\d+ [^ ]+ ([0-9a-fA-F]+)\t([^\0]+)\0\z/ =~ line
|
173
|
+
rev = $1
|
174
|
+
path = $2
|
175
|
+
h[path] = rev
|
176
|
+
end
|
177
|
+
}
|
178
|
+
h
|
179
|
+
end
|
180
|
+
|
181
|
+
def git_path_sort(ary)
|
182
|
+
ary.sort_by {|path|
|
183
|
+
path.gsub(%r{([^/]+)(/|\z)}) {
|
184
|
+
if $2 == ""
|
185
|
+
if $1 == '.'
|
186
|
+
"A"
|
187
|
+
else
|
188
|
+
"B#{$1}"
|
189
|
+
end
|
190
|
+
else
|
191
|
+
"C#{$1}\0"
|
192
|
+
end
|
193
|
+
}
|
194
|
+
}
|
195
|
+
end
|
196
|
+
|
197
|
+
class GitHub
|
198
|
+
def initialize(user, project)
|
199
|
+
@user = user
|
200
|
+
@project = project
|
201
|
+
end
|
202
|
+
|
203
|
+
def commit_uri(commit_hash)
|
204
|
+
# http://github.com/brixen/rubyspec/commit/b8f8eb6765afe915f2ecfdbbe59a53e6393d6865
|
205
|
+
"http://github.com/#{@user}/#{@project}/commit/#{commit_hash}"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def git_print_logs(old_head, logs, urigen=nil)
|
210
|
+
if !old_head
|
211
|
+
puts "last commit:"
|
212
|
+
end
|
213
|
+
logs.each {|commit_hash, title_line|
|
214
|
+
if urigen
|
215
|
+
commit = urigen.commit_uri(commit_hash)
|
216
|
+
else
|
217
|
+
commit = commit_hash
|
218
|
+
end
|
219
|
+
line = "COMMIT #{title_line}\t#{commit}"
|
220
|
+
puts line
|
221
|
+
}
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
# Copyright (C) 2006,2007,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 'fileutils'
|
26
|
+
require "uri"
|
27
|
+
|
28
|
+
module ChkBuild; end # for testing
|
29
|
+
|
30
|
+
class ChkBuild::ViewVC
|
31
|
+
def initialize(uri, old=false)
|
32
|
+
@uri = uri
|
33
|
+
@old = old
|
34
|
+
end
|
35
|
+
|
36
|
+
def rev_uri(r)
|
37
|
+
revision = @old ? 'rev' : 'revision'
|
38
|
+
extend_uri("", [['view', 'rev'], [revision, r.to_s]]).to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def markup_uri(d, f, r)
|
42
|
+
pathrev = @old ? 'rev' : 'pathrev'
|
43
|
+
extend_uri("/#{d}/#{f}", [['view', 'markup'], [pathrev, r.to_s]]).to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def dir_uri(d, f, r)
|
47
|
+
pathrev = @old ? 'rev' : 'pathrev'
|
48
|
+
extend_uri("/#{d}/#{f}", [[pathrev, r.to_s]]).to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
def diff_uri(d, f, r1, r2)
|
52
|
+
pathrev = @old ? 'rev' : 'pathrev'
|
53
|
+
extend_uri("/#{d}/#{f}", [
|
54
|
+
['p1', "#{d}/#{f}"],
|
55
|
+
['r1', r1.to_s],
|
56
|
+
['r2', r2.to_s],
|
57
|
+
[pathrev, r2.to_s]]).to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
def extend_uri(path, params)
|
61
|
+
uri = URI.parse(@uri)
|
62
|
+
uri.path = uri.path + Escape.uri_path(path).to_s
|
63
|
+
query = Escape.html_form(params).to_s
|
64
|
+
(uri.query || '').split(/[;&]/).each {|param| query << ';' << param }
|
65
|
+
uri.query = query
|
66
|
+
uri
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class ChkBuild::Build
|
71
|
+
def svn(svnroot, rep_dir, working_dir, opts={})
|
72
|
+
network_access {
|
73
|
+
svn_internal(svnroot, rep_dir, working_dir, opts)
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def svn_internal(svnroot, rep_dir, working_dir, opts={})
|
78
|
+
url = svnroot + '/' + rep_dir
|
79
|
+
opts = opts.dup
|
80
|
+
opts[:section] ||= 'svn'
|
81
|
+
if opts[:viewvc]||opts[:viewcvs]
|
82
|
+
viewvc = ChkBuild::ViewVC.new(opts[:viewvc]||opts[:viewcvs], opts[:viewcvs]!=nil)
|
83
|
+
else
|
84
|
+
viewvc = nil
|
85
|
+
end
|
86
|
+
if File.exist?(working_dir) && File.exist?("#{working_dir}/.svn")
|
87
|
+
Dir.chdir(working_dir) {
|
88
|
+
self.run "svn", "cleanup", opts
|
89
|
+
opts[:section] = nil
|
90
|
+
h1 = svn_revisions
|
91
|
+
self.run "svn", "update", "-q", opts
|
92
|
+
h2 = svn_revisions
|
93
|
+
svn_print_changes(h1, h2, viewvc, rep_dir)
|
94
|
+
}
|
95
|
+
else
|
96
|
+
if File.exist?(working_dir)
|
97
|
+
FileUtils.rm_rf(working_dir)
|
98
|
+
end
|
99
|
+
h1 = h2 = nil
|
100
|
+
if File.identical?(self.build_dir, '.') &&
|
101
|
+
!(ts = self.build_time_sequence - [self.start_time]).empty? &&
|
102
|
+
File.directory?(old_working_dir = self.target_dir + ts.last + working_dir)
|
103
|
+
Dir.chdir(old_working_dir) {
|
104
|
+
h1 = svn_revisions
|
105
|
+
}
|
106
|
+
end
|
107
|
+
self.run "svn", "checkout", "-q", url, working_dir, opts
|
108
|
+
Dir.chdir(working_dir) {
|
109
|
+
h2 = svn_revisions
|
110
|
+
svn_print_changes(h1, h2, viewvc, rep_dir) if h1
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def svn_revisions
|
116
|
+
IO.popen("svn status -v") {|f|
|
117
|
+
svn_parse_status(f)
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
def svn_parse_status(f)
|
122
|
+
h = {}
|
123
|
+
f.each {|line|
|
124
|
+
if /\d+\s+(\d+)\s+\S+\s+(.+)/ =~ line
|
125
|
+
rev = $1.to_i
|
126
|
+
path = $2
|
127
|
+
dir = File.directory?(path)
|
128
|
+
path << '/' if dir && path != '.'
|
129
|
+
h[path] = [rev, dir]
|
130
|
+
end
|
131
|
+
}
|
132
|
+
h
|
133
|
+
end
|
134
|
+
|
135
|
+
def svn_path_sort(ary)
|
136
|
+
ary.sort_by {|path|
|
137
|
+
path.gsub(%r{([^/]+)(/|\z)}) {
|
138
|
+
if $2 == ""
|
139
|
+
if $1 == '.'
|
140
|
+
"A"
|
141
|
+
else
|
142
|
+
"B#{$1}"
|
143
|
+
end
|
144
|
+
else
|
145
|
+
"C#{$1}\0"
|
146
|
+
end
|
147
|
+
}
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
def svn_rev_uri(viewvc, r)
|
152
|
+
return nil if !viewvc
|
153
|
+
viewvc.rev_uri(r)
|
154
|
+
end
|
155
|
+
|
156
|
+
def svn_markup_uri(viewvc, d, f, r)
|
157
|
+
return nil if !viewvc
|
158
|
+
viewvc.markup_uri(d, f, r)
|
159
|
+
end
|
160
|
+
|
161
|
+
def svn_dir_uri(viewvc, d, f, r)
|
162
|
+
return nil if !viewvc
|
163
|
+
viewvc.dir_uri(d, f, r)
|
164
|
+
end
|
165
|
+
|
166
|
+
def svn_diff_uri(viewvc, d, f, r1, r2)
|
167
|
+
return nil if !viewvc
|
168
|
+
viewvc.diff_uri(d, f, r1, r2)
|
169
|
+
end
|
170
|
+
|
171
|
+
def svn_print_changes(h1, h2, viewvc=nil, rep_dir=nil)
|
172
|
+
top_r1, _ = h1['.']
|
173
|
+
top_r2, _ = h2['.']
|
174
|
+
h1.delete '.'
|
175
|
+
h2.delete '.'
|
176
|
+
return if top_r1 == top_r2
|
177
|
+
svn_print_chg_line('.', top_r1, top_r2, svn_rev_uri(viewvc, top_r2))
|
178
|
+
svn_path_sort(h1.keys|h2.keys).each {|f|
|
179
|
+
r1, d1 = h1[f] || ['none', nil]
|
180
|
+
r2, d2 = h2[f] || ['none', nil]
|
181
|
+
next if r1 == r2 # no changes
|
182
|
+
next if d1 && d2 # skip directory changes
|
183
|
+
if !d1 && !d2 && r1 != 'none' && r2 != 'none'
|
184
|
+
svn_print_chg_line(f, r1, r2,
|
185
|
+
svn_diff_uri(viewvc, rep_dir, f, top_r1, top_r2))
|
186
|
+
else
|
187
|
+
svn_print_del_line(f, r1,
|
188
|
+
d1 ? svn_dir_uri(viewvc, rep_dir, f, top_r1) :
|
189
|
+
svn_markup_uri(viewvc, rep_dir, f, top_r1)) if r1 != 'none'
|
190
|
+
svn_print_add_line(f, r2,
|
191
|
+
d2 ? svn_dir_uri(viewvc, rep_dir, f, top_r2) :
|
192
|
+
svn_markup_uri(viewvc, rep_dir, f, top_r2)) if r2 != 'none'
|
193
|
+
end
|
194
|
+
}
|
195
|
+
end
|
196
|
+
|
197
|
+
def svn_print_chg_line(f, r1, r2, uri)
|
198
|
+
line = "CHG #{f}\t#{r1}->#{r2}"
|
199
|
+
line << "\t" << uri.to_s if uri
|
200
|
+
puts line
|
201
|
+
end
|
202
|
+
|
203
|
+
def svn_print_del_line(f, r, uri)
|
204
|
+
line = "DEL #{f}\t#{r}->none"
|
205
|
+
line << "\t" << uri.to_s if uri
|
206
|
+
puts line
|
207
|
+
end
|
208
|
+
|
209
|
+
def svn_print_add_line(f, r, uri)
|
210
|
+
line = "ADD #{f}\tnone->#{r}"
|
211
|
+
line << "\t" << uri.to_s if uri
|
212
|
+
puts line
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|