summaryrefslogtreecommitdiff
path: root/tool/downloader.rb
diff options
context:
space:
mode:
Diffstat (limited to 'tool/downloader.rb')
-rw-r--r--tool/downloader.rb132
1 files changed, 104 insertions, 28 deletions
diff --git a/tool/downloader.rb b/tool/downloader.rb
index e9ea00c4c4..3a91ea0b93 100644
--- a/tool/downloader.rb
+++ b/tool/downloader.rb
@@ -36,6 +36,12 @@ else
end
class Downloader
+ def self.find(dlname)
+ constants.find do |name|
+ return const_get(name) if dlname.casecmp(name.to_s) == 0
+ end
+ end
+
def self.https=(https)
@@https = https
end
@@ -48,13 +54,18 @@ class Downloader
@@https
end
+ def self.get_option(argv, options)
+ false
+ end
+
class GNU < self
def self.download(name, *rest)
if https?
begin
super("https://cdn.jsdelivr.net/gh/gcc-mirror/gcc@master/#{name}", name, *rest)
rescue => e
- STDERR.puts "Download failed (#{e.message}), try another URL"
+ m1, m2 = e.message.split("\n", 2)
+ STDERR.puts "Download failed (#{m1}), try another URL\n#{m2}"
super("https://raw.githubusercontent.com/gcc-mirror/gcc/master/#{name}", name, *rest)
end
else
@@ -68,6 +79,9 @@ class Downloader
require 'rubygems'
options = options.dup
options[:ssl_ca_cert] = Dir.glob(File.expand_path("../lib/rubygems/ssl_certs/**/*.pem", File.dirname(__FILE__)))
+ if Gem::Version.new(name[/-\K[^-]*(?=\.gem\z)/]).prerelease?
+ options[:ignore_http_client_errors] = true
+ end
super("https://rubygems.org/downloads/#{name}", name, dir, since, options)
end
end
@@ -78,6 +92,21 @@ class Downloader
INDEX = {} # cache index file information across files in the same directory
UNICODE_PUBLIC = "https://www.unicode.org/Public/"
+ def self.get_option(argv, options)
+ case argv[0]
+ when '--unicode-beta'
+ options[:unicode_beta] = argv[1]
+ argv.shift(2)
+ true
+ when /\A--unicode-beta=(.*)/m
+ options[:unicode_beta] = $1
+ argv.shift
+ true
+ else
+ super
+ end
+ end
+
def self.download(name, dir = nil, since = true, options = {})
options = options.dup
unicode_beta = options.delete(:unicode_beta)
@@ -173,7 +202,6 @@ class Downloader
options = options.dup
url = URI(url)
dryrun = options.delete(:dryrun)
- options.delete(:unicode_beta) # just to be on the safe side for gems and gcc
if name
file = Pathname.new(under(dir, name))
@@ -211,17 +239,30 @@ class Downloader
$stdout.print "downloading #{name} ... "
$stdout.flush
end
+ mtime = nil
+ ignore_http_client_errors = options.delete(:ignore_http_client_errors)
+ options = options.merge(http_options(file, since.nil? ? true : since))
begin
data = with_retry(10) do
- url.read(options.merge(http_options(file, since.nil? ? true : since)))
+ data = url.read(options)
+ if mtime = data.meta["last-modified"]
+ mtime = Time.httpdate(mtime)
+ end
+ data
end
rescue OpenURI::HTTPError => http_error
- if http_error.message =~ /^304 / # 304 Not Modified
+ case http_error.message
+ when /^304 / # 304 Not Modified
if $VERBOSE
$stdout.puts "#{name} not modified"
$stdout.flush
end
return file.to_path
+ when /^40/ # Net::HTTPClientError: 403 Forbidden, 404 Not Found
+ if ignore_http_client_errors
+ puts "Ignore #{url}: #{http_error.message}"
+ return file.to_path
+ end
end
raise
rescue Timeout::Error
@@ -237,16 +278,14 @@ class Downloader
end
raise
end
- mtime = nil
dest = (cache_save && cache && !cache.exist? ? cache : file)
dest.parent.mkpath
+ dest.unlink if dest.symlink? && !dest.exist?
dest.open("wb", 0600) do |f|
f.write(data)
f.chmod(mode_for(data))
- mtime = data.meta["last-modified"]
end
if mtime
- mtime = httpdate(mtime)
dest.utime(mtime, mtime)
end
if $VERBOSE
@@ -285,7 +324,16 @@ class Downloader
return true if cache.eql?(file)
if /cygwin/ !~ RUBY_PLATFORM or /winsymlink:nativestrict/ =~ ENV['CYGWIN']
begin
- file.make_symlink(cache.relative_path_from(file.parent))
+ link = cache.relative_path_from(file.parent)
+ rescue ArgumentError
+ abs = cache.expand_path
+ link = abs.relative_path_from(file.parent.expand_path)
+ if link.to_s.count("/") > abs.to_s.count("/")
+ link = abs
+ end
+ end
+ begin
+ file.make_symlink(link)
rescue SystemCallError
else
if verbose
@@ -328,7 +376,7 @@ class Downloader
times = 0
begin
block.call
- rescue Errno::ETIMEDOUT, SocketError, OpenURI::HTTPError, Net::ReadTimeout, Net::OpenTimeout => e
+ rescue Errno::ETIMEDOUT, SocketError, OpenURI::HTTPError, Net::ReadTimeout, Net::OpenTimeout, ArgumentError => e
raise if e.is_a?(OpenURI::HTTPError) && e.message !~ /^50[023] / # retry only 500, 502, 503 for http error
times += 1
if times <= max_times
@@ -348,45 +396,73 @@ Downloader.https = https.freeze
if $0 == __FILE__
since = true
options = {}
+ dl = nil
+ (args = []).singleton_class.__send__(:define_method, :downloader?) do |arg|
+ !dl and args.empty? and (dl = Downloader.find(arg))
+ end
until ARGV.empty?
+ if ARGV[0] == '--'
+ ARGV.shift
+ break if ARGV.empty?
+ ARGV.shift if args.downloader? ARGV[0]
+ args.concat(ARGV)
+ break
+ end
+
+ if dl and dl.get_option(ARGV, options)
+ # the downloader dealt with the arguments, and should be removed
+ # from ARGV.
+ next
+ end
+
case ARGV[0]
- when '-d'
+ when '-d', '--destdir'
+ ## -d, --destdir DIRECTORY Download into the directory
destdir = ARGV[1]
ARGV.shift
- when '-p'
- # strip directory names from the name to download, and add the
- # prefix instead.
+ when '-p', '--prefix'
+ ## -p, --prefix Strip directory names from the name to download,
+ ## and add the prefix instead.
prefix = ARGV[1]
ARGV.shift
- when '-e'
+ when '-e', '--exist', '--non-existent-only'
+ ## -e, --exist, --non-existent-only Skip already existent files.
since = nil
- when '-a'
+ when '-a', '--always'
+ ## -a, --always Download all files.
since = false
- when '-n', '--dryrun'
+ when '-u', '--update', '--if-modified'
+ ## -u, --update, --if-modified Download newer files only.
+ since = true
+ when '-n', '--dry-run', '--dryrun'
+ ## -n, --dry-run Do not download actually.
options[:dryrun] = true
when '--cache-dir'
+ ## --cache-dir DIRECTORY Cache downloaded files in the directory.
options[:cache_dir] = ARGV[1]
ARGV.shift
- when '--unicode-beta'
- options[:unicode_beta] = ARGV[1]
- ARGV.shift
when /\A--cache-dir=(.*)/m
options[:cache_dir] = $1
+ when /\A--help\z/
+ ## --help Print this message
+ puts "Usage: #$0 [options] relative-url..."
+ File.foreach(__FILE__) do |line|
+ line.sub!(/^ *## /, "") or next
+ break if line.chomp!.empty?
+ opt, desc = line.split(/ {2,}/, 2)
+ printf " %-28s %s\n", opt, desc
+ end
+ exit
when /\A-/
abort "#{$0}: unknown option #{ARGV[0]}"
else
- break
+ args << ARGV[0] unless args.downloader? ARGV[0]
end
ARGV.shift
end
- dl = Downloader.constants.find do |name|
- ARGV[0].casecmp(name.to_s) == 0
- end unless ARGV.empty?
$VERBOSE = true
if dl
- dl = Downloader.const_get(dl)
- ARGV.shift
- ARGV.each do |name|
+ args.each do |name|
dir = destdir
if prefix
name = name.sub(/\A\.\//, '')
@@ -406,7 +482,7 @@ if $0 == __FILE__
dl.download(name, dir, since, options)
end
else
- abort "usage: #{$0} url name" unless ARGV.size == 2
- Downloader.download(ARGV[0], ARGV[1], destdir, since, options)
+ abort "usage: #{$0} url name" unless args.size == 2
+ Downloader.download(args[0], args[1], destdir, since, options)
end
end