mini_portile2 2.1.0 → 2.8.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/FUNDING.yml +2 -0
- data/.github/workflows/ci.yml +163 -0
- data/.github/workflows/downstream.yml +66 -0
- data/.gitignore +4 -3
- data/CHANGELOG.md +166 -0
- data/Gemfile +2 -0
- data/README.md +164 -9
- data/Rakefile +4 -6
- data/SECURITY.md +13 -0
- data/lib/mini_portile2/mini_portile.rb +384 -96
- data/lib/mini_portile2/mini_portile_cmake.rb +119 -0
- data/lib/mini_portile2/version.rb +1 -1
- data/lib/mini_portile2.rb +1 -0
- data/mini_portile2.gemspec +28 -22
- data/test/assets/gpg-fixtures/data +1 -0
- data/test/assets/gpg-fixtures/data.asc +9 -0
- data/test/assets/gpg-fixtures/data.invalid.asc +9 -0
- data/test/assets/pkgconf/libxml2/libxml-2.0.pc +13 -0
- data/test/assets/pkgconf/libxslt/libexslt.pc +13 -0
- data/test/assets/pkgconf/libxslt/libxslt.pc +13 -0
- data/test/assets/test-cmake-1.0/CMakeLists.txt +7 -0
- data/test/assets/test-cmake-1.0/hello.c +4 -0
- data/test/assets/test-download-archive.tar.gz +0 -0
- data/test/helper.rb +38 -3
- data/test/test_activate.rb +139 -0
- data/test/test_cmake.rb +266 -0
- data/test/test_cook.rb +134 -12
- data/test/test_digest.rb +147 -3
- data/test/test_download.rb +22 -19
- data/test/test_execute.rb +40 -0
- data/test/test_mkmf_config.rb +202 -0
- data/test/test_proxy.rb +9 -8
- data/test/test_recipe.rb +18 -0
- metadata +70 -27
- data/.travis.yml +0 -11
- data/appveyor.yml +0 -24
@@ -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
|
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
|
-
|
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
|
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
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
99
|
-
|
100
|
-
File.open(
|
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
|
-
|
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
|
-
|
213
|
+
cache_file = File.join(tmp_path, 'configure.options_cache')
|
131
214
|
|
132
|
-
|
133
|
-
|
215
|
+
stored_options = File.exist?(cache_file) ? File.read(cache_file) : ""
|
216
|
+
current_options = computed_options.to_s
|
134
217
|
|
135
|
-
(
|
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
|
-
|
147
|
-
|
148
|
-
|
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' =>
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
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',
|
575
|
+
execute('extract', tar_command(file, target) , {:cd => Dir.pwd, :initial_message => false})
|
332
576
|
end
|
333
577
|
|
334
|
-
|
335
|
-
|
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
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
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
|
-
|
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
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
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
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
output(
|
364
|
-
output("
|
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
|
-
|
422
|
-
|
423
|
-
|
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
|
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
|
-
|
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
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
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
|
-
|
504
|
-
|
505
|
-
|
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
|