1#!/usr/bin/ruby -s 2# -*- coding: us-ascii -*- 3require 'uri' 4require 'digest/md5' 5require 'digest/sha2' 6require 'fileutils' 7require 'tmpdir' 8STDOUT.sync = true 9 10$exported = nil if $exported == "" 11$archname = nil if $archname == "" 12$keep_temp ||= nil 13$patch_file ||= nil 14 15def usage 16 <<USAGE 17usage: #{File.basename $0} [option...] new-directory-to-save [version ...] 18options: 19 -exported=PATH make snapshot from already exported working directory 20 -archname=NAME make the basename of snapshots NAME 21 -keep_temp keep temporary working directory 22 -patch_file=PATCH apply PATCH file after export 23version: 24 trunk, stable, branches/*, tags/*, X.Y.Z, X.Y.Z-pL 25each versions may be followed by optional @revision. 26USAGE 27end 28 29ENV["LC_ALL"] = ENV["LANG"] = "C" 30SVNURL = URI.parse("http://svn.ruby-lang.org/repos/ruby/") 31RUBY_VERSION_PATTERN = /^\#define\s+RUBY_VERSION\s+"([\d.]+)"/ 32 33ENV["VPATH"] ||= "include/ruby" 34YACC = ENV["YACC"] ||= "bison" 35ENV["BASERUBY"] ||= "ruby" 36ENV["RUBY"] ||= "ruby" 37ENV["MV"] ||= "mv" 38ENV["RM"] ||= "rm -f" 39ENV["MINIRUBY"] ||= "ruby" 40ENV["PROGRAM"] ||= "ruby" 41 42class String 43 # for older ruby 44 alias bytesize size unless method_defined?(:bytesize) 45end 46 47class Dir 48 def self.mktmpdir(path) 49 path = File.join(tmpdir, path+"-#{$$}-#{rand(100000)}") 50 begin 51 mkdir(path) 52 rescue Errno::EEXIST 53 path.succ! 54 retry 55 end 56 path 57 end unless respond_to?(:mktmpdir) 58end 59 60$patch_file &&= File.expand_path($patch_file) 61path = ENV["PATH"].split(File::PATH_SEPARATOR) 62%w[YACC BASERUBY RUBY MV MINIRUBY].each do |var| 63 cmd = ENV[var] 64 unless path.any? {|dir| 65 file = File.expand_path(cmd, dir) 66 File.file?(file) and File.executable?(file) 67 } 68 abort "#{File.basename $0}: #{var} command not found - #{cmd}" 69 end 70end 71 72%w[BASERUBY RUBY MINIRUBY].each do |var| 73 `#{ENV[var]} --disable-gem -e1 2>&1` 74 if $?.success? 75 ENV[var] += ' --disable-gem' 76 end 77end 78 79if $help or $_help 80 puts usage 81 exit 82end 83unless destdir = ARGV.shift 84 abort usage 85end 86revisions = ARGV.empty? ? ["trunk"] : ARGV 87unless tmp = $exported 88 FileUtils.mkpath(destdir) 89 destdir = File.expand_path(destdir) 90 tmp = Dir.mktmpdir("ruby-snapshot") 91 FileUtils.mkpath(tmp) 92 at_exit { 93 Dir.chdir "/" 94 FileUtils.rm_rf(tmp) 95 } unless $keep_temp 96end 97Dir.chdir tmp 98 99def package(rev, destdir) 100 patchlevel = false 101 if revision = rev[/@(\d+)\z/, 1] 102 rev = $` 103 end 104 case rev 105 when /\Atrunk\z/, /\Abranches\//, /\Atags\// 106 url = SVNURL + rev 107 when /\Astable\z/ 108 url = SVNURL + "branches/" 109 url = url + `svn ls #{url}`[/.*^(ruby_\d+_\d+)\//m, 1] 110 when /\A(.*)\.(.*)\.(.*)-(preview|rc)(\d+)/ 111 tag = "#{$4}#{$5}" 112 url = SVNURL + "tags/v#{$1}_#{$2}_#{$3}_#{$4}#{$5}" 113 when /\A(.*)\.(.*)\.(.*)-p(\d+)/ 114 patchlevel = true 115 tag = "p#{$4}" 116 url = SVNURL + "tags/v#{$1}_#{$2}_#{$3}_#{$4}" 117 when /\./ 118 url = SVNURL + "branches/ruby_#{rev.tr('.', '_')}" 119 else 120 warn "#{$0}: unknown version - #{rev}" 121 return 122 end 123 revision ||= `svn info #{url} 2>&1`[/Last Changed Rev: (\d+)/, 1] 124 version = nil 125 unless revision 126 url = SVNURL + "trunk" 127 version = `svn cat #{url + "version.h"}`[RUBY_VERSION_PATTERN, 1] 128 unless rev == version 129 warn "#{$0}: #{rev} not found" 130 return 131 end 132 revision = `svn info #{url}`[/Last Changed Rev: (\d+)/, 1] 133 end 134 v = nil 135 if $exported 136 if String === $exported 137 v = $exported 138 end 139 else 140 v = "ruby" 141 puts "Exporting #{rev}@#{revision}" 142 IO.popen("svn export -r #{revision} #{url} #{v}") do |pipe| 143 pipe.each {|line| /^A/ =~ line or print line} 144 end 145 unless $?.success? 146 warn("Export failed") 147 return 148 end 149 end 150 151 if !File.directory?(v) 152 v = Dir.glob("ruby-*").select(&File.method(:directory?)) 153 v.size == 1 or abort "not exported" 154 v = v[0] 155 end 156 open("#{v}/revision.h", "wb") {|f| f.puts "#define RUBY_REVISION #{revision}"} 157 open("#{v}/.revision.time", "wb") {} 158 version ||= (versionhdr = IO.read("#{v}/version.h"))[RUBY_VERSION_PATTERN, 1] 159 version or return 160 if patchlevel 161 versionhdr ||= IO.read("#{v}/version.h") 162 patchlevel = versionhdr[/^\#define\s+RUBY_PATCHLEVEL\s+(\d+)/, 1] 163 tag = (patchlevel ? "p#{patchlevel}" : "r#{revision}") 164 else 165 tag ||= "r#{revision}" 166 end 167 unless v == $exported 168 n = "ruby-#{version}-#{tag}" 169 File.directory?(n) or File.rename v, n 170 v = n 171 end 172 system("patch -d #{v} -p0 -i #{$patch_file}") if $patch_file 173 "take a breath, and go ahead".scan(/./) {|c|print c; sleep(c == "," ? 0.7 : 0.05)}; puts 174 def (clean = []).add(n) push(n); n end 175 Dir.chdir(v) do 176 File.open(clean.add("cross.rb"), "w") do |f| 177 f.puts "Object.__send__(:remove_const, :CROSS_COMPILING) if defined?(CROSS_COMPILING)" 178 f.puts "CROSS_COMPILING=true" 179 end 180 unless File.exist?("configure") 181 print "creating configure..." 182 unless system("autoconf") 183 puts " failed" 184 return 185 end 186 puts " done" 187 end 188 clean.add("autom4te.cache") 189 print "creating prerequisites..." 190 if File.file?("common.mk") && /^prereq/ =~ commonmk = IO.read("common.mk") 191 puts 192 extout = clean.add('tmp') 193 File.open(clean.add("config.status"), "w") {|f| 194 f.puts "s,@configure_args@,|#_!!_#|,g" 195 f.puts "s,@EXTOUT@,|#_!!_#|#{extout},g" 196 f.puts "s,@bindir@,|#_!!_#|,g" 197 f.puts "s,@ruby_install_name@,|#_!!_#|,g" 198 f.puts "s,@ARCH_FLAG@,|#_!!_#|,g" 199 f.puts "s,@CFLAGS@,|#_!!_#|,g" 200 f.puts "s,@CPPFLAGS@,|#_!!_#|,g" 201 f.puts "s,@LDFLAGS@,|#_!!_#|,g" 202 f.puts "s,@DLDFLAGS@,|#_!!_#|,g" 203 f.puts "s,@LIBEXT@,|#_!!_#|a,g" 204 f.puts "s,@OBJEXT@,|#_!!_#|o,g" 205 f.puts "s,@EXEEXT@,|#_!!_#|,g" 206 f.puts "s,@LIBRUBY@,|#_!!_#|libruby.a,g" 207 f.puts "s,@LIBRUBY_A@,|#_!!_#|libruby.a,g" 208 f.puts "s,@RM@,|#_!!_#|rm -f,g" 209 f.puts "s,@CP@,|#_!!_#|cp,g" 210 f.puts "s,@rubyarchdir@,|#_!!_#|,g" 211 } 212 FileUtils.mkpath(hdrdir = "#{extout}/include/ruby") 213 File.open("#{hdrdir}/config.h", "w") {} 214 miniruby = ENV['MINIRUBY'] + " -r./cross" 215 IO.popen("make -f - prereq"\ 216 " srcdir=. CHDIR=cd PATH_SEPARATOR='#{File::PATH_SEPARATOR}'"\ 217 " IFCHANGE=tool/ifchange MAKEDIRS='mkdir -p'"\ 218 " 'MINIRUBY=#{miniruby}' 'RUBY=#{ENV["RUBY"]}'", "w") do |f| 219 f.puts(IO.read("Makefile.in").gsub(/^@.*\n/, '').gsub(/@([A-Za-z_]\w*)@/) {ENV[$1]}) 220 f.puts(commonmk.gsub(/\{[^{}]*\}/, "")) 221 end 222 clean.push("rbconfig.rb", ".rbconfig.time", "enc.mk") 223 print "prerequisites" 224 else 225 system("#{YACC} -o parse.c parse.y") 226 end 227 FileUtils.rm_rf(clean) 228 unless $?.success? 229 puts " failed" 230 return 231 end 232 puts " done" 233 end 234 235 if v == "." 236 v = File.basename(Dir.pwd) 237 Dir.chdir ".." 238 else 239 Dir.chdir(File.dirname(v)) 240 v = File.basename(v) 241 end 242 243 return [["bzip tarball", ".tar.bz2", %w"tar cjf"], 244 ["gzip tarball", ".tar.gz", %w"tar czf"], 245 ["zip archive", ".zip", %w"zip -qr"] 246 ].collect do |mesg, ext, cmd| 247 file = File.join(destdir, "#{$archname||v}#{ext}") 248 print "creating #{mesg}... #{file}" 249 if system(*(cmd + [file, v])) 250 puts " done" 251 file 252 else 253 puts " failed" 254 nil 255 end 256 end.compact 257ensure 258 FileUtils.rm_rf(v) if v and !$exported and !$keep_temp 259end 260 261revisions.collect {|rev| package(rev, destdir)}.flatten.each do |name| 262 name or next 263 str = open(name, "rb") {|f| f.read} 264 md5 = Digest::MD5.hexdigest str 265 sha = Digest::SHA256.hexdigest str 266 puts "* #{name}" 267 puts " SIZE: #{str.bytesize} bytes" 268 puts " MD5: #{md5}" 269 puts " SHA256: #{sha}" 270 puts 271end 272 273# vim:fileencoding=US-ASCII sw=2 ts=4 noexpandtab ff=unix 274