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.
- 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
|