mini_portile2 2.1.0 → 2.8.7

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.
@@ -1,14 +1,14 @@
1
1
  require 'rbconfig'
2
2
  require 'net/http'
3
3
  require 'net/https'
4
- require 'net/ftp'
5
4
  require 'fileutils'
6
5
  require 'tempfile'
7
- require 'digest/md5'
6
+ require 'digest'
8
7
  require 'open-uri'
9
8
  require 'cgi'
10
9
  require 'rbconfig'
11
10
  require 'shellwords'
11
+ require 'open3'
12
12
 
13
13
  # Monkey patch for Net::HTTP by ruby open-uri fix:
14
14
  # https://github.com/ruby/ruby/commit/58835a9
@@ -28,23 +28,105 @@ class Net::HTTP
28
28
  end
29
29
  end
30
30
 
31
+ $MINI_PORTILE_STATIC_LIBS = {}
32
+
31
33
  class MiniPortile
32
- attr_reader :name, :version, :original_host
34
+ DEFAULT_TIMEOUT = 10
35
+
36
+ attr_reader :name, :version, :original_host, :source_directory
33
37
  attr_writer :configure_options
34
38
  attr_accessor :host, :files, :patch_files, :target, :logger
35
39
 
36
- def initialize(name, version)
40
+ def self.windows?
41
+ target_os =~ /mswin|mingw/
42
+ end
43
+
44
+ # GNU MinGW compiled Ruby?
45
+ def self.mingw?
46
+ target_os =~ /mingw/
47
+ end
48
+
49
+ # MS Visual-C compiled Ruby?
50
+ def self.mswin?
51
+ target_os =~ /mswin/
52
+ end
53
+
54
+ def self.darwin?
55
+ target_os =~ /darwin/
56
+ end
57
+
58
+ def self.freebsd?
59
+ target_os =~ /freebsd/
60
+ end
61
+
62
+ def self.openbsd?
63
+ target_os =~ /openbsd/
64
+ end
65
+
66
+ def self.linux?
67
+ target_os =~ /linux/
68
+ end
69
+
70
+ def self.solaris?
71
+ target_os =~ /solaris/
72
+ end
73
+
74
+ def self.target_os
75
+ RbConfig::CONFIG['target_os']
76
+ end
77
+
78
+ def self.target_cpu
79
+ RbConfig::CONFIG['target_cpu']
80
+ end
81
+
82
+ def self.native_path(path)
83
+ path = File.expand_path(path)
84
+ if File::ALT_SEPARATOR
85
+ path.tr(File::SEPARATOR, File::ALT_SEPARATOR)
86
+ else
87
+ path
88
+ end
89
+ end
90
+
91
+ def self.posix_path(path)
92
+ path = File.expand_path(path)
93
+ if File::ALT_SEPARATOR
94
+ "/" + path.tr(File::ALT_SEPARATOR, File::SEPARATOR).tr(":", File::SEPARATOR)
95
+ else
96
+ path
97
+ end
98
+ end
99
+
100
+ def initialize(name, version, **kwargs)
37
101
  @name = name
38
102
  @version = version
39
103
  @target = 'ports'
40
104
  @files = []
41
105
  @patch_files = []
42
106
  @log_files = {}
43
- @logger = STDOUT
107
+ @logger = kwargs[:logger] || STDOUT
108
+ @source_directory = nil
109
+
110
+ @cc_command = kwargs[:cc_command] || kwargs[:gcc_command]
111
+ @cxx_command = kwargs[:cxx_command]
112
+ @make_command = kwargs[:make_command]
113
+ @open_timeout = kwargs[:open_timeout] || DEFAULT_TIMEOUT
114
+ @read_timeout = kwargs[:read_timeout] || DEFAULT_TIMEOUT
44
115
 
45
116
  @original_host = @host = detect_host
46
117
  end
47
118
 
119
+ def source_directory=(path)
120
+ @source_directory = posix_path(path)
121
+ end
122
+
123
+ def prepare_build_directory
124
+ raise "source_directory is not set" if source_directory.nil?
125
+ output "Building #{@name} from source at '#{source_directory}'"
126
+ FileUtils.mkdir_p(File.join(tmp_path, [name, version].join("-")))
127
+ FileUtils.rm_rf(port_path) # make sure we always re-install
128
+ end
129
+
48
130
  def download
49
131
  files_hashs.each do |file|
50
132
  download_file(file[:url], file[:local_path])
@@ -54,6 +136,7 @@ class MiniPortile
54
136
 
55
137
  def extract
56
138
  files_hashs.each do |file|
139
+ verify_file(file)
57
140
  extract_file(file[:local_path], tmp_path)
58
141
  end
59
142
  end
@@ -66,9 +149,9 @@ class MiniPortile
66
149
  when which('git')
67
150
  lambda { |file|
68
151
  message "Running git apply with #{file}... "
69
- # By --work-tree=. git-apply uses the current directory as
70
- # the project root and will not search upwards for .git.
71
- execute('patch', ["git", "--work-tree=.", "apply", "--whitespace=warn", file], :initial_message => false)
152
+ Dir.mktmpdir do |tmp_git_dir|
153
+ execute('patch', ["git", "--git-dir=#{tmp_git_dir}", "--work-tree=.", "apply", "--whitespace=warn", file], :initial_message => false)
154
+ end
72
155
  }
73
156
  when which('patch')
74
157
  lambda { |file|
@@ -95,16 +178,16 @@ class MiniPortile
95
178
  def configure
96
179
  return if configured?
97
180
 
98
- md5_file = File.join(tmp_path, 'configure.md5')
99
- digest = Digest::MD5.hexdigest(computed_options.to_s)
100
- File.open(md5_file, "w") { |f| f.write digest }
181
+ FileUtils.mkdir_p(tmp_path)
182
+ cache_file = File.join(tmp_path, 'configure.options_cache')
183
+ File.open(cache_file, "w") { |f| f.write computed_options.to_s }
101
184
 
185
+ command = Array(File.join((source_directory || "."), "configure"))
102
186
  if RUBY_PLATFORM=~/mingw|mswin/
103
187
  # Windows doesn't recognize the shebang.
104
- execute('configure', %w(sh ./configure) + computed_options)
105
- else
106
- execute('configure', %w(./configure) + computed_options)
188
+ command.unshift("sh")
107
189
  end
190
+ execute('configure', command + computed_options, altlog: "config.log")
108
191
  end
109
192
 
110
193
  def compile
@@ -125,14 +208,14 @@ class MiniPortile
125
208
  end
126
209
 
127
210
  def configured?
128
- configure = File.join(work_path, 'configure')
211
+ configure = File.join((source_directory || work_path), 'configure')
129
212
  makefile = File.join(work_path, 'Makefile')
130
- md5_file = File.join(tmp_path, 'configure.md5')
213
+ cache_file = File.join(tmp_path, 'configure.options_cache')
131
214
 
132
- stored_md5 = File.exist?(md5_file) ? File.read(md5_file) : ""
133
- current_md5 = Digest::MD5.hexdigest(computed_options.to_s)
215
+ stored_options = File.exist?(cache_file) ? File.read(cache_file) : ""
216
+ current_options = computed_options.to_s
134
217
 
135
- (current_md5 == stored_md5) && newer?(makefile, configure)
218
+ (current_options == stored_options) && newer?(makefile, configure)
136
219
  end
137
220
 
138
221
  def installed?
@@ -143,9 +226,13 @@ class MiniPortile
143
226
  end
144
227
 
145
228
  def cook
146
- download unless downloaded?
147
- extract
148
- patch
229
+ if source_directory
230
+ prepare_build_directory
231
+ else
232
+ download unless downloaded?
233
+ extract
234
+ patch
235
+ end
149
236
  configure unless configured?
150
237
  compile
151
238
  install unless installed?
@@ -154,19 +241,15 @@ class MiniPortile
154
241
  end
155
242
 
156
243
  def activate
157
- lib_path = File.join(port_path, "lib")
158
244
  vars = {
159
245
  'PATH' => File.join(port_path, 'bin'),
160
- 'CPATH' => File.join(port_path, 'include'),
161
- 'LIBRARY_PATH' => lib_path
246
+ 'CPATH' => include_path,
247
+ 'LIBRARY_PATH' => lib_path,
162
248
  }.reject { |env, path| !File.directory?(path) }
163
249
 
164
250
  output "Activating #{@name} #{@version} (from #{port_path})..."
165
251
  vars.each do |var, path|
166
- full_path = File.expand_path(path)
167
-
168
- # turn into a valid Windows path (if required)
169
- full_path.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
252
+ full_path = native_path(path)
170
253
 
171
254
  # save current variable value
172
255
  old_value = ENV[var] || ''
@@ -178,7 +261,7 @@ class MiniPortile
178
261
 
179
262
  # rely on LDFLAGS when cross-compiling
180
263
  if File.exist?(lib_path) && (@host != @original_host)
181
- full_path = File.expand_path(lib_path)
264
+ full_path = native_path(lib_path)
182
265
 
183
266
  old_value = ENV.fetch("LDFLAGS", "")
184
267
 
@@ -188,11 +271,129 @@ class MiniPortile
188
271
  end
189
272
  end
190
273
 
274
+ # pkg: the pkg-config file name (without the .pc extension)
275
+ # dir: inject the directory path for the pkg-config file (probably only useful for tests)
276
+ # static: the name of the static library archive (without the "lib" prefix or the file extension), or nil for dynamic linking
277
+ #
278
+ # we might be able to be terribly clever and infer the name of the static archive file, but
279
+ # unfortunately projects have so much freedom in what they can report (for name, for libs, etc.)
280
+ # that it feels unreliable to try to do so, so I'm preferring to just have the developer make it
281
+ # explicit.
282
+ def mkmf_config(pkg: nil, dir: nil, static: nil)
283
+ require "mkmf"
284
+
285
+ if pkg
286
+ dir ||= File.join(lib_path, "pkgconfig")
287
+ pcfile = File.join(dir, "#{pkg}.pc")
288
+ unless File.exist?(pcfile)
289
+ raise ArgumentError, "pkg-config file '#{pcfile}' does not exist"
290
+ end
291
+
292
+ output "Configuring MakeMakefile for #{File.basename(pcfile)} (in #{File.dirname(pcfile)})\n"
293
+
294
+ # on macos, pkg-config will not return --cflags without this
295
+ ENV["PKG_CONFIG_ALLOW_SYSTEM_CFLAGS"] = "t"
296
+
297
+ # append to PKG_CONFIG_PATH as we go, so later pkg-config files can depend on earlier ones
298
+ ENV["PKG_CONFIG_PATH"] = [ENV["PKG_CONFIG_PATH"], dir].compact.join(File::PATH_SEPARATOR)
299
+
300
+ incflags = minimal_pkg_config(pcfile, "cflags-only-I")
301
+ cflags = minimal_pkg_config(pcfile, "cflags-only-other")
302
+ if static
303
+ ldflags = minimal_pkg_config(pcfile, "libs-only-L", "static")
304
+ libflags = minimal_pkg_config(pcfile, "libs-only-l", "static")
305
+ else
306
+ ldflags = minimal_pkg_config(pcfile, "libs-only-L")
307
+ libflags = minimal_pkg_config(pcfile, "libs-only-l")
308
+ end
309
+ else
310
+ output "Configuring MakeMakefile for #{@name} #{@version} (from #{path})\n"
311
+
312
+ lib_name = name.sub(/\Alib/, "") # TODO: use delete_prefix when we no longer support ruby 2.4
313
+
314
+ incflags = Dir.exist?(include_path) ? "-I#{include_path}" : ""
315
+ cflags = ""
316
+ ldflags = Dir.exist?(lib_path) ? "-L#{lib_path}" : ""
317
+ libflags = Dir.exist?(lib_path) ? "-l#{lib_name}" : ""
318
+ end
319
+
320
+ if static
321
+ libdir = lib_path
322
+ if pcfile
323
+ pcfile_libdir = minimal_pkg_config(pcfile, "variable=libdir").strip
324
+ libdir = pcfile_libdir unless pcfile_libdir.empty?
325
+ end
326
+
327
+ #
328
+ # keep track of the libraries we're statically linking against, and fix up ldflags and
329
+ # libflags to make sure we link statically against the recipe's libaries.
330
+ #
331
+ # this avoids the unintentionally dynamically linking against system libraries, and makes sure
332
+ # that if multiple pkg-config files reference each other that we are able to intercept flags
333
+ # from dependent packages that reference the static archive.
334
+ #
335
+ $MINI_PORTILE_STATIC_LIBS[static] = libdir
336
+ static_ldflags = $MINI_PORTILE_STATIC_LIBS.values.map { |v| "-L#{v}" }
337
+ static_libflags = $MINI_PORTILE_STATIC_LIBS.keys.map { |v| "-l#{v}" }
338
+
339
+ # remove `-L#{libdir}` and `-lfoo`. we don't need them since we link against the static
340
+ # archive using the full path.
341
+ ldflags = ldflags.shellsplit.reject { |f| static_ldflags.include?(f) }.shelljoin
342
+ libflags = libflags.shellsplit.reject { |f| static_libflags.include?(f) }.shelljoin
343
+
344
+ # prepend the full path to the static archive to the linker flags
345
+ static_archive = File.join(libdir, "lib#{static}.#{$LIBEXT}")
346
+ libflags = [static_archive, libflags].join(" ").strip
347
+ end
348
+
349
+ # prefer this package by prepending to search paths and library flags
350
+ #
351
+ # convert the ldflags into a list of directories and append to $LIBPATH (instead of just using
352
+ # $LDFLAGS) to ensure we get the `-Wl,-rpath` linker flag for re-finding shared libraries.
353
+ $INCFLAGS = [incflags, $INCFLAGS].join(" ").strip
354
+ libpaths = ldflags.shellsplit.map { |f| f.sub(/\A-L/, "") }
355
+ $LIBPATH = libpaths | $LIBPATH
356
+ $libs = [libflags, $libs].join(" ").strip
357
+
358
+ # prefer this package's compiler flags by appending them to the command line
359
+ $CFLAGS = [$CFLAGS, cflags].join(" ").strip
360
+ $CXXFLAGS = [$CXXFLAGS, cflags].join(" ").strip
361
+ end
362
+
191
363
  def path
192
364
  File.expand_path(port_path)
193
365
  end
194
366
 
195
- private
367
+ def include_path
368
+ File.join(path, "include")
369
+ end
370
+
371
+ def lib_path
372
+ File.join(path, "lib")
373
+ end
374
+
375
+ def cc_cmd
376
+ (ENV["CC"] || @cc_command || RbConfig::CONFIG["CC"] || "gcc").dup
377
+ end
378
+ alias :gcc_cmd :cc_cmd
379
+
380
+ def cxx_cmd
381
+ (ENV["CXX"] || @cxx_command || RbConfig::CONFIG["CXX"] || "g++").dup
382
+ end
383
+
384
+ def make_cmd
385
+ (ENV["MAKE"] || @make_command || ENV["make"] || "make").dup
386
+ end
387
+
388
+ private
389
+
390
+ def native_path(path)
391
+ MiniPortile.native_path(path)
392
+ end
393
+
394
+ def posix_path(path)
395
+ MiniPortile.posix_path(path)
396
+ end
196
397
 
197
398
  def tmp_path
198
399
  "tmp/#{@host}/ports/#{@name}/#{@version}"
@@ -247,16 +448,55 @@ private
247
448
  end
248
449
  end
249
450
 
451
+ KEYRING_NAME = "mini_portile_keyring.gpg"
452
+
250
453
  def verify_file(file)
251
- digest = case
252
- when exp=file[:sha256] then Digest::SHA256
253
- when exp=file[:sha1] then Digest::SHA1
254
- when exp=file[:md5] then Digest::MD5
255
- end
256
- if digest
257
- is = digest.file(file[:local_path]).hexdigest
258
- unless is == exp.downcase
259
- raise "Downloaded file '#{file[:local_path]}' has wrong hash: expected: #{exp} is: #{is}"
454
+ if file.has_key?(:gpg)
455
+ gpg = file[:gpg]
456
+
457
+ signature_url = gpg[:signature_url] || "#{file[:url]}.asc"
458
+ signature_file = file[:local_path] + ".asc"
459
+ # download the signature file
460
+ download_file(signature_url, signature_file)
461
+
462
+ gpg_exe = which('gpg2') || which('gpg') || raise("Neither GPG nor GPG2 is installed")
463
+
464
+ # import the key into our own keyring
465
+ gpg_error = nil
466
+ gpg_status = Open3.popen3(gpg_exe, "--status-fd", "1", "--no-default-keyring", "--keyring", KEYRING_NAME, "--import") do |gpg_in, gpg_out, gpg_err, _thread|
467
+ gpg_in.write gpg[:key]
468
+ gpg_in.close
469
+ gpg_error = gpg_err.read
470
+ gpg_out.read
471
+ end
472
+ key_ids = gpg_status.scan(/\[GNUPG:\] IMPORT_OK \d+ (?<key_id>[0-9a-f]+)/i).map(&:first)
473
+ raise "invalid gpg key provided:\n#{gpg_error}" if key_ids.empty?
474
+
475
+ begin
476
+ # verify the signature against our keyring
477
+ gpg_status, gpg_error, _status = Open3.capture3(gpg_exe, "--status-fd", "1", "--no-default-keyring", "--keyring", KEYRING_NAME, "--verify", signature_file, file[:local_path])
478
+
479
+ raise "signature mismatch:\n#{gpg_error}" unless gpg_status.match(/^\[GNUPG:\] VALIDSIG/)
480
+ ensure
481
+ # remove the key from our keyring
482
+ key_ids.each do |key_id|
483
+ IO.popen([gpg_exe, "--batch", "--yes", "--no-default-keyring", "--keyring", KEYRING_NAME, "--delete-keys", key_id], &:read)
484
+ raise "unable to delete the imported key" unless $?.exitstatus==0
485
+ end
486
+ end
487
+
488
+
489
+ else
490
+ digest = case
491
+ when exp=file[:sha256] then Digest::SHA256
492
+ when exp=file[:sha1] then Digest::SHA1
493
+ when exp=file[:md5] then Digest::MD5
494
+ end
495
+ if digest
496
+ is = digest.file(file[:local_path]).hexdigest
497
+ unless is == exp.downcase
498
+ raise "Downloaded file '#{file[:local_path]}' has wrong hash: expected: #{exp} is: #{is}"
499
+ end
260
500
  end
261
501
  end
262
502
  end
@@ -268,27 +508,6 @@ private
268
508
  }
269
509
  end
270
510
 
271
- def tar_exe
272
- @@tar_exe ||= begin
273
- %w[gtar bsdtar tar basic-bsdtar].find { |c|
274
- which(c)
275
- }
276
- end
277
- end
278
-
279
- def tar_compression_switch(filename)
280
- case File.extname(filename)
281
- when '.gz', '.tgz'
282
- 'z'
283
- when '.bz2', '.tbz2'
284
- 'j'
285
- when '.Z'
286
- 'Z'
287
- else
288
- ''
289
- end
290
- end
291
-
292
511
  # From: http://stackoverflow.com/a/5471032/7672
293
512
  # Thanks, Mislav!
294
513
  #
@@ -315,6 +534,8 @@ private
315
534
  output = `#{gcc_cmd} -v 2>&1`
316
535
  if m = output.match(/^Target\: (.*)$/)
317
536
  @detect_host = m[1]
537
+ else
538
+ @detect_host = nil
318
539
  end
319
540
 
320
541
  @detect_host
@@ -323,32 +544,68 @@ private
323
544
  end
324
545
  end
325
546
 
547
+ TAR_EXECUTABLES = %w[gtar bsdtar tar basic-bsdtar]
548
+ def tar_exe
549
+ @@tar_exe ||= begin
550
+ TAR_EXECUTABLES.find { |c|
551
+ which(c)
552
+ } or raise("tar not found - please make sure that one of the following commands is in the PATH: #{TAR_EXECUTABLES.join(", ")}")
553
+ end
554
+ end
555
+
556
+ def tar_command(file, target)
557
+ case File.extname(file)
558
+ when '.gz', '.tgz'
559
+ [tar_exe, 'xzf', file, '-C', target]
560
+ when '.bz2', '.tbz2'
561
+ [tar_exe, 'xjf', file, '-C', target]
562
+ when '.xz'
563
+ # NOTE: OpenBSD's tar command does not support the -J option
564
+ "xzcat #{file.shellescape} | #{tar_exe.shellescape} xf - -C #{target.shellescape}"
565
+ else
566
+ [tar_exe, 'xf', file, '-C', target]
567
+ end
568
+ end
569
+
326
570
  def extract_file(file, target)
327
571
  filename = File.basename(file)
328
572
  FileUtils.mkdir_p target
329
573
 
330
574
  message "Extracting #{filename} into #{target}... "
331
- execute('extract', [tar_exe, "#{tar_compression_switch(filename)}xf", file, "-C", target], {:cd => Dir.pwd, :initial_message => false})
575
+ execute('extract', tar_command(file, target) , {:cd => Dir.pwd, :initial_message => false})
332
576
  end
333
577
 
334
- def execute(action, command, options={})
335
- log_out = log_file(action)
578
+ # command could be an array of args, or one string containing a command passed to the shell. See
579
+ # Process.spawn for more information.
580
+ def execute(action, command, command_opts={})
581
+ opt_message = command_opts.fetch(:initial_message, true)
582
+ opt_debug = command_opts.fetch(:debug, false)
583
+ opt_cd = command_opts.fetch(:cd) { work_path }
584
+ opt_env = command_opts.fetch(:env) { Hash.new }
585
+ opt_altlog = command_opts.fetch(:altlog, nil)
336
586
 
337
- Dir.chdir (options.fetch(:cd){ work_path }) do
338
- if options.fetch(:initial_message){ true }
339
- message "Running '#{action}' for #{@name} #{@version}... "
340
- end
587
+ log_out = log_file(action)
588
+
589
+ Dir.chdir(opt_cd) do
590
+ output "DEBUG: env is #{opt_env.inspect}" if opt_debug
591
+ output "DEBUG: command is #{command.inspect}" if opt_debug
592
+ message "Running '#{action}' for #{@name} #{@version}... " if opt_message
341
593
 
342
594
  if Process.respond_to?(:spawn) && ! RbConfig.respond_to?(:java)
343
- args = [command].flatten + [{[:out, :err]=>[log_out, "a"]}]
595
+ options = {[:out, :err]=>[log_out, "a"]}
596
+ output "DEBUG: options are #{options.inspect}" if opt_debug
597
+ args = [opt_env, command, options].flatten
344
598
  pid = spawn(*args)
345
599
  Process.wait(pid)
346
600
  else
347
- redirected = if command.kind_of?(Array)
348
- %Q{#{command.map(&:shellescape).join(" ")} > #{log_out.shellescape} 2>&1}
349
- else
350
- %Q{#{command} > #{log_out.shellescape} 2>&1}
351
- end
601
+ env_args = opt_env.map { |k,v| "#{k}=#{v}".shellescape }.join(" ")
602
+ c = if command.kind_of?(Array)
603
+ command.map(&:shellescape).join(" ")
604
+ else
605
+ command
606
+ end
607
+ redirected = %Q{env #{env_args} #{c} > #{log_out.shellescape} 2>&1}
608
+ output "DEBUG: final command is #{redirected.inspect}" if opt_debug
352
609
  system redirected
353
610
  end
354
611
 
@@ -356,12 +613,12 @@ private
356
613
  output "OK"
357
614
  return true
358
615
  else
359
- if File.exist? log_out
360
- output "ERROR, review '#{log_out}' to see what happened. Last lines are:"
361
- output("=" * 72)
362
- log_lines = File.readlines(log_out)
363
- output(log_lines[-[log_lines.length, 20].min .. -1])
364
- output("=" * 72)
616
+ output "ERROR. Please review logs to see what happened:\n"
617
+ [log_out, opt_altlog].compact.each do |log|
618
+ next unless File.exist?(log)
619
+ output("----- contents of '#{log}' -----")
620
+ output(File.read(log))
621
+ output("----- end of file -----")
365
622
  end
366
623
  raise "Failed to complete #{action} task"
367
624
  end
@@ -412,16 +669,21 @@ private
412
669
  def download_file_http(url, full_path, count = 3)
413
670
  filename = File.basename(full_path)
414
671
  with_tempfile(filename, full_path) do |temp_file|
415
- progress = 0
416
672
  total = 0
417
673
  params = {
418
674
  "Accept-Encoding" => 'identity',
419
675
  :content_length_proc => lambda{|length| total = length },
420
676
  :progress_proc => lambda{|bytes|
421
- new_progress = (bytes * 100) / total
422
- message "\rDownloading %s (%3d%%) " % [filename, new_progress]
423
- progress = new_progress
424
- }
677
+ if total
678
+ new_progress = (bytes * 100) / total
679
+ message "\rDownloading %s (%3d%%) " % [filename, new_progress]
680
+ else
681
+ # Content-Length is unavailable because Transfer-Encoding is chunked
682
+ message "\rDownloading %s " % [filename]
683
+ end
684
+ },
685
+ :open_timeout => @open_timeout,
686
+ :read_timeout => @read_timeout,
425
687
  }
426
688
  proxy_uri = URI.parse(url).scheme.downcase == 'https' ?
427
689
  ENV["https_proxy"] :
@@ -434,6 +696,7 @@ private
434
696
  [proxy_uri, proxy_user, proxy_pass]
435
697
  end
436
698
  end
699
+
437
700
  begin
438
701
  OpenURI.open_uri(url, 'rb', params) do |io|
439
702
  temp_file << io.read
@@ -442,8 +705,15 @@ private
442
705
  rescue OpenURI::HTTPRedirect => redirect
443
706
  raise "Too many redirections for the original URL, halting." if count <= 0
444
707
  count = count - 1
445
- return download_file(redirect.url, full_path, count - 1)
708
+ return download_file(redirect.url, full_path, count-1)
446
709
  rescue => e
710
+ count = count - 1
711
+ @logger.puts "#{count} retrie(s) left for #{filename} (#{e.message})"
712
+ if count > 0
713
+ sleep 1
714
+ return download_file_http(url, full_path, count)
715
+ end
716
+
447
717
  output e.message
448
718
  return false
449
719
  end
@@ -456,17 +726,18 @@ private
456
726
  end
457
727
 
458
728
  def download_file_ftp(uri, full_path)
729
+ require "net/ftp"
459
730
  filename = File.basename(uri.path)
460
731
  with_tempfile(filename, full_path) do |temp_file|
461
- progress = 0
462
732
  total = 0
463
733
  params = {
464
734
  :content_length_proc => lambda{|length| total = length },
465
735
  :progress_proc => lambda{|bytes|
466
736
  new_progress = (bytes * 100) / total
467
737
  message "\rDownloading %s (%3d%%) " % [filename, new_progress]
468
- progress = new_progress
469
- }
738
+ },
739
+ :open_timeout => @open_timeout,
740
+ :read_timeout => @read_timeout,
470
741
  }
471
742
  if ENV["ftp_proxy"]
472
743
  _, userinfo, _p_host, _p_port = URI.split(ENV['ftp_proxy'])
@@ -481,6 +752,8 @@ private
481
752
  end
482
753
  output
483
754
  end
755
+ rescue LoadError
756
+ raise LoadError, "Ruby #{RUBY_VERSION} does not provide the net-ftp gem, please add it as a dependency if you need to use FTP"
484
757
  rescue Net::FTPError
485
758
  return false
486
759
  end
@@ -495,13 +768,28 @@ private
495
768
  FileUtils.mv temp_file.path, full_path, :force => true
496
769
  end
497
770
 
498
- def gcc_cmd
499
- cc = ENV["CC"] || RbConfig::CONFIG["CC"] || "gcc"
500
- return cc.dup
501
- end
771
+ #
772
+ # this minimal version of pkg_config is based on ruby 29dc9378 (2023-01-09)
773
+ #
774
+ # specifically with the fix from b90e56e6 to support multiple pkg-config options, and removing
775
+ # code paths that aren't helpful for mini-portile's use case of parsing pc files.
776
+ #
777
+ def minimal_pkg_config(pkg, *pcoptions)
778
+ if pcoptions.empty?
779
+ raise ArgumentError, "no pkg-config options are given"
780
+ end
502
781
 
503
- def make_cmd
504
- m = ENV['MAKE'] || ENV['make'] || 'make'
505
- return m.dup
782
+ if ($PKGCONFIG ||=
783
+ (pkgconfig = MakeMakefile.with_config("pkg-config") {MakeMakefile.config_string("PKG_CONFIG") || "pkg-config"}) &&
784
+ MakeMakefile.find_executable0(pkgconfig) && pkgconfig)
785
+ pkgconfig = $PKGCONFIG
786
+ else
787
+ raise RuntimeError, "pkg-config is not found"
788
+ end
789
+
790
+ pcoptions = Array(pcoptions).map { |o| "--#{o}" }
791
+ response = IO.popen([pkgconfig, *pcoptions, pkg], err:[:child, :out], &:read)
792
+ raise RuntimeError, response unless $?.success?
793
+ response.strip
506
794
  end
507
795
  end