mini_portile2 2.4.0 → 2.8.5
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 +4 -4
- data/.github/FUNDING.yml +2 -0
- data/.github/workflows/ci.yml +88 -0
- data/.gitignore +4 -5
- data/CHANGELOG.md +117 -0
- data/Gemfile +2 -0
- data/README.md +102 -2
- data/Rakefile +2 -6
- data/SECURITY.md +13 -0
- data/lib/mini_portile2/mini_portile.rb +286 -58
- data/lib/mini_portile2/mini_portile_cmake.rb +111 -7
- data/lib/mini_portile2/version.rb +1 -1
- data/mini_portile2.gemspec +24 -23
- 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-download-archive.tar.gz +0 -0
- data/test/helper.rb +16 -0
- data/test/test_activate.rb +139 -0
- data/test/test_cmake.rb +217 -20
- data/test/test_cook.rb +55 -0
- data/test/test_download.rb +7 -5
- data/test/test_execute.rb +39 -0
- data/test/test_mkmf_config.rb +202 -0
- data/test/test_recipe.rb +18 -0
- metadata +42 -31
- data/.concourse.yml +0 -83
- data/.travis.yml +0 -15
- data/appveyor.yml +0 -25
- data/concourse/mini_portile.yml +0 -141
- data/concourse/tasks/rake-test/task.ps1 +0 -11
- data/concourse/tasks/rake-test/task.sh +0 -13
@@ -1,7 +1,6 @@
|
|
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
6
|
require 'digest'
|
@@ -28,16 +27,76 @@ class Net::HTTP
|
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
30
|
+
$MINI_PORTILE_STATIC_LIBS = {}
|
31
|
+
|
31
32
|
class MiniPortile
|
32
|
-
|
33
|
+
DEFAULT_TIMEOUT = 10
|
34
|
+
|
35
|
+
attr_reader :name, :version, :original_host, :source_directory
|
33
36
|
attr_writer :configure_options
|
34
37
|
attr_accessor :host, :files, :patch_files, :target, :logger
|
35
38
|
|
36
39
|
def self.windows?
|
37
|
-
|
40
|
+
target_os =~ /mswin|mingw/
|
41
|
+
end
|
42
|
+
|
43
|
+
# GNU MinGW compiled Ruby?
|
44
|
+
def self.mingw?
|
45
|
+
target_os =~ /mingw/
|
46
|
+
end
|
47
|
+
|
48
|
+
# MS Visual-C compiled Ruby?
|
49
|
+
def self.mswin?
|
50
|
+
target_os =~ /mswin/
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.darwin?
|
54
|
+
target_os =~ /darwin/
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.freebsd?
|
58
|
+
target_os =~ /freebsd/
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.openbsd?
|
62
|
+
target_os =~ /openbsd/
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.linux?
|
66
|
+
target_os =~ /linux/
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.solaris?
|
70
|
+
target_os =~ /solaris/
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.target_os
|
74
|
+
RbConfig::CONFIG['target_os']
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.target_cpu
|
78
|
+
RbConfig::CONFIG['target_cpu']
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.native_path(path)
|
82
|
+
path = File.expand_path(path)
|
83
|
+
if File::ALT_SEPARATOR
|
84
|
+
path.tr(File::SEPARATOR, File::ALT_SEPARATOR)
|
85
|
+
else
|
86
|
+
path
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.posix_path(path)
|
91
|
+
path = File.expand_path(path)
|
92
|
+
if File::ALT_SEPARATOR
|
93
|
+
"/" + path.tr(File::ALT_SEPARATOR, File::SEPARATOR).tr(":", File::SEPARATOR)
|
94
|
+
else
|
95
|
+
path
|
96
|
+
end
|
38
97
|
end
|
39
98
|
|
40
|
-
def initialize(name, version)
|
99
|
+
def initialize(name, version, **kwargs)
|
41
100
|
@name = name
|
42
101
|
@version = version
|
43
102
|
@target = 'ports'
|
@@ -45,10 +104,27 @@ class MiniPortile
|
|
45
104
|
@patch_files = []
|
46
105
|
@log_files = {}
|
47
106
|
@logger = STDOUT
|
107
|
+
@source_directory = nil
|
108
|
+
|
109
|
+
@gcc_command = kwargs[:gcc_command]
|
110
|
+
@make_command = kwargs[:make_command]
|
111
|
+
@open_timeout = kwargs[:open_timeout] || DEFAULT_TIMEOUT
|
112
|
+
@read_timeout = kwargs[:read_timeout] || DEFAULT_TIMEOUT
|
48
113
|
|
49
114
|
@original_host = @host = detect_host
|
50
115
|
end
|
51
116
|
|
117
|
+
def source_directory=(path)
|
118
|
+
@source_directory = posix_path(path)
|
119
|
+
end
|
120
|
+
|
121
|
+
def prepare_build_directory
|
122
|
+
raise "source_directory is not set" if source_directory.nil?
|
123
|
+
output "Building #{@name} from source at '#{source_directory}'"
|
124
|
+
FileUtils.mkdir_p(File.join(tmp_path, [name, version].join("-")))
|
125
|
+
FileUtils.rm_rf(port_path) # make sure we always re-install
|
126
|
+
end
|
127
|
+
|
52
128
|
def download
|
53
129
|
files_hashs.each do |file|
|
54
130
|
download_file(file[:url], file[:local_path])
|
@@ -71,9 +147,9 @@ class MiniPortile
|
|
71
147
|
when which('git')
|
72
148
|
lambda { |file|
|
73
149
|
message "Running git apply with #{file}... "
|
74
|
-
|
75
|
-
|
76
|
-
|
150
|
+
Dir.mktmpdir do |tmp_git_dir|
|
151
|
+
execute('patch', ["git", "--git-dir=#{tmp_git_dir}", "--work-tree=.", "apply", "--whitespace=warn", file], :initial_message => false)
|
152
|
+
end
|
77
153
|
}
|
78
154
|
when which('patch')
|
79
155
|
lambda { |file|
|
@@ -100,15 +176,16 @@ class MiniPortile
|
|
100
176
|
def configure
|
101
177
|
return if configured?
|
102
178
|
|
179
|
+
FileUtils.mkdir_p(tmp_path)
|
103
180
|
cache_file = File.join(tmp_path, 'configure.options_cache')
|
104
181
|
File.open(cache_file, "w") { |f| f.write computed_options.to_s }
|
105
182
|
|
183
|
+
command = Array(File.join((source_directory || "."), "configure"))
|
106
184
|
if RUBY_PLATFORM=~/mingw|mswin/
|
107
185
|
# Windows doesn't recognize the shebang.
|
108
|
-
|
109
|
-
else
|
110
|
-
execute('configure', %w(./configure) + computed_options)
|
186
|
+
command.unshift("sh")
|
111
187
|
end
|
188
|
+
execute('configure', command + computed_options, altlog: "config.log")
|
112
189
|
end
|
113
190
|
|
114
191
|
def compile
|
@@ -129,7 +206,7 @@ class MiniPortile
|
|
129
206
|
end
|
130
207
|
|
131
208
|
def configured?
|
132
|
-
configure = File.join(work_path, 'configure')
|
209
|
+
configure = File.join((source_directory || work_path), 'configure')
|
133
210
|
makefile = File.join(work_path, 'Makefile')
|
134
211
|
cache_file = File.join(tmp_path, 'configure.options_cache')
|
135
212
|
|
@@ -147,9 +224,13 @@ class MiniPortile
|
|
147
224
|
end
|
148
225
|
|
149
226
|
def cook
|
150
|
-
|
151
|
-
|
152
|
-
|
227
|
+
if source_directory
|
228
|
+
prepare_build_directory
|
229
|
+
else
|
230
|
+
download unless downloaded?
|
231
|
+
extract
|
232
|
+
patch
|
233
|
+
end
|
153
234
|
configure unless configured?
|
154
235
|
compile
|
155
236
|
install unless installed?
|
@@ -158,19 +239,15 @@ class MiniPortile
|
|
158
239
|
end
|
159
240
|
|
160
241
|
def activate
|
161
|
-
lib_path = File.join(port_path, "lib")
|
162
242
|
vars = {
|
163
243
|
'PATH' => File.join(port_path, 'bin'),
|
164
|
-
'CPATH' =>
|
165
|
-
'LIBRARY_PATH' => lib_path
|
244
|
+
'CPATH' => include_path,
|
245
|
+
'LIBRARY_PATH' => lib_path,
|
166
246
|
}.reject { |env, path| !File.directory?(path) }
|
167
247
|
|
168
248
|
output "Activating #{@name} #{@version} (from #{port_path})..."
|
169
249
|
vars.each do |var, path|
|
170
|
-
full_path =
|
171
|
-
|
172
|
-
# turn into a valid Windows path (if required)
|
173
|
-
full_path.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
|
250
|
+
full_path = native_path(path)
|
174
251
|
|
175
252
|
# save current variable value
|
176
253
|
old_value = ENV[var] || ''
|
@@ -182,7 +259,7 @@ class MiniPortile
|
|
182
259
|
|
183
260
|
# rely on LDFLAGS when cross-compiling
|
184
261
|
if File.exist?(lib_path) && (@host != @original_host)
|
185
|
-
full_path =
|
262
|
+
full_path = native_path(lib_path)
|
186
263
|
|
187
264
|
old_value = ENV.fetch("LDFLAGS", "")
|
188
265
|
|
@@ -192,11 +269,126 @@ class MiniPortile
|
|
192
269
|
end
|
193
270
|
end
|
194
271
|
|
272
|
+
# pkg: the pkg-config file name (without the .pc extension)
|
273
|
+
# dir: inject the directory path for the pkg-config file (probably only useful for tests)
|
274
|
+
# static: the name of the static library archive (without the "lib" prefix or the file extension), or nil for dynamic linking
|
275
|
+
#
|
276
|
+
# we might be able to be terribly clever and infer the name of the static archive file, but
|
277
|
+
# unfortunately projects have so much freedom in what they can report (for name, for libs, etc.)
|
278
|
+
# that it feels unreliable to try to do so, so I'm preferring to just have the developer make it
|
279
|
+
# explicit.
|
280
|
+
def mkmf_config(pkg: nil, dir: nil, static: nil)
|
281
|
+
require "mkmf"
|
282
|
+
|
283
|
+
if pkg
|
284
|
+
dir ||= File.join(lib_path, "pkgconfig")
|
285
|
+
pcfile = File.join(dir, "#{pkg}.pc")
|
286
|
+
unless File.exist?(pcfile)
|
287
|
+
raise ArgumentError, "pkg-config file '#{pcfile}' does not exist"
|
288
|
+
end
|
289
|
+
|
290
|
+
output "Configuring MakeMakefile for #{File.basename(pcfile)} (in #{File.dirname(pcfile)})\n"
|
291
|
+
|
292
|
+
# on macos, pkg-config will not return --cflags without this
|
293
|
+
ENV["PKG_CONFIG_ALLOW_SYSTEM_CFLAGS"] = "t"
|
294
|
+
|
295
|
+
# append to PKG_CONFIG_PATH as we go, so later pkg-config files can depend on earlier ones
|
296
|
+
ENV["PKG_CONFIG_PATH"] = [ENV["PKG_CONFIG_PATH"], dir].compact.join(File::PATH_SEPARATOR)
|
297
|
+
|
298
|
+
incflags = minimal_pkg_config(pcfile, "cflags-only-I")
|
299
|
+
cflags = minimal_pkg_config(pcfile, "cflags-only-other")
|
300
|
+
if static
|
301
|
+
ldflags = minimal_pkg_config(pcfile, "libs-only-L", "static")
|
302
|
+
libflags = minimal_pkg_config(pcfile, "libs-only-l", "static")
|
303
|
+
else
|
304
|
+
ldflags = minimal_pkg_config(pcfile, "libs-only-L")
|
305
|
+
libflags = minimal_pkg_config(pcfile, "libs-only-l")
|
306
|
+
end
|
307
|
+
else
|
308
|
+
output "Configuring MakeMakefile for #{@name} #{@version} (from #{path})\n"
|
309
|
+
|
310
|
+
lib_name = name.sub(/\Alib/, "") # TODO: use delete_prefix when we no longer support ruby 2.4
|
311
|
+
|
312
|
+
incflags = Dir.exist?(include_path) ? "-I#{include_path}" : ""
|
313
|
+
cflags = ""
|
314
|
+
ldflags = Dir.exist?(lib_path) ? "-L#{lib_path}" : ""
|
315
|
+
libflags = Dir.exist?(lib_path) ? "-l#{lib_name}" : ""
|
316
|
+
end
|
317
|
+
|
318
|
+
if static
|
319
|
+
libdir = lib_path
|
320
|
+
if pcfile
|
321
|
+
variables = minimal_pkg_config(pcfile, "print-variables").split("\n").map(&:strip)
|
322
|
+
if variables.include?("libdir")
|
323
|
+
libdir = minimal_pkg_config(pcfile, "variable=libdir")
|
324
|
+
end
|
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
|
+
|
195
363
|
def path
|
196
364
|
File.expand_path(port_path)
|
197
365
|
end
|
198
366
|
|
199
|
-
|
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 gcc_cmd
|
376
|
+
(ENV["CC"] || @gcc_command || RbConfig::CONFIG["CC"] || "gcc").dup
|
377
|
+
end
|
378
|
+
|
379
|
+
def make_cmd
|
380
|
+
(ENV["MAKE"] || @make_command || ENV["make"] || "make").dup
|
381
|
+
end
|
382
|
+
|
383
|
+
private
|
384
|
+
|
385
|
+
def native_path(path)
|
386
|
+
MiniPortile.native_path(path)
|
387
|
+
end
|
388
|
+
|
389
|
+
def posix_path(path)
|
390
|
+
MiniPortile.posix_path(path)
|
391
|
+
end
|
200
392
|
|
201
393
|
def tmp_path
|
202
394
|
"tmp/#{@host}/ports/#{@name}/#{@version}"
|
@@ -270,15 +462,18 @@ private
|
|
270
462
|
io.close_write
|
271
463
|
io.read
|
272
464
|
end
|
273
|
-
|
465
|
+
key_ids = gpg_status.scan(/\[GNUPG:\] IMPORT_OK \d+ (?<key_id>[0-9a-f]+)/i).map(&:first)
|
466
|
+
raise "invalid gpg key provided" if key_ids.empty?
|
274
467
|
|
275
468
|
# verify the signature against our keyring
|
276
469
|
gpg_status = IO.popen([gpg_exe, "--status-fd", "1", "--no-default-keyring", "--keyring", KEYRING_NAME, "--verify", signature_file, file[:local_path]], &:read)
|
277
470
|
|
278
471
|
# remove the key from our keyring
|
279
|
-
|
472
|
+
key_ids.each do |key_id|
|
473
|
+
IO.popen([gpg_exe, "--batch", "--yes", "--no-default-keyring", "--keyring", KEYRING_NAME, "--delete-keys", key_id], &:read)
|
474
|
+
raise "unable to delete the imported key" unless $?.exitstatus==0
|
475
|
+
end
|
280
476
|
|
281
|
-
raise "unable to delete the imported key" unless $?.exitstatus==0
|
282
477
|
raise "signature mismatch" unless gpg_status.match(/^\[GNUPG:\] VALIDSIG/)
|
283
478
|
|
284
479
|
else
|
@@ -318,6 +513,8 @@ private
|
|
318
513
|
'z'
|
319
514
|
when '.bz2', '.tbz2'
|
320
515
|
'j'
|
516
|
+
when '.xz'
|
517
|
+
'J'
|
321
518
|
when '.Z'
|
322
519
|
'Z'
|
323
520
|
else
|
@@ -367,24 +564,37 @@ private
|
|
367
564
|
execute('extract', [tar_exe, "#{tar_compression_switch(filename)}xf", file, "-C", target], {:cd => Dir.pwd, :initial_message => false})
|
368
565
|
end
|
369
566
|
|
370
|
-
|
371
|
-
|
567
|
+
# command could be an array of args, or one string containing a command passed to the shell. See
|
568
|
+
# Process.spawn for more information.
|
569
|
+
def execute(action, command, command_opts={})
|
570
|
+
opt_message = command_opts.fetch(:initial_message, true)
|
571
|
+
opt_debug = command_opts.fetch(:debug, false)
|
572
|
+
opt_cd = command_opts.fetch(:cd) { work_path }
|
573
|
+
opt_env = command_opts.fetch(:env) { Hash.new }
|
574
|
+
opt_altlog = command_opts.fetch(:altlog, nil)
|
372
575
|
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
576
|
+
log_out = log_file(action)
|
577
|
+
|
578
|
+
Dir.chdir(opt_cd) do
|
579
|
+
output "DEBUG: env is #{opt_env.inspect}" if opt_debug
|
580
|
+
output "DEBUG: command is #{command.inspect}" if opt_debug
|
581
|
+
message "Running '#{action}' for #{@name} #{@version}... " if opt_message
|
377
582
|
|
378
583
|
if Process.respond_to?(:spawn) && ! RbConfig.respond_to?(:java)
|
379
|
-
|
584
|
+
options = {[:out, :err]=>[log_out, "a"]}
|
585
|
+
output "DEBUG: options are #{options.inspect}" if opt_debug
|
586
|
+
args = [opt_env, command, options].flatten
|
380
587
|
pid = spawn(*args)
|
381
588
|
Process.wait(pid)
|
382
589
|
else
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
590
|
+
env_args = opt_env.map { |k,v| "#{k}=#{v}".shellescape }.join(" ")
|
591
|
+
c = if command.kind_of?(Array)
|
592
|
+
command.map(&:shellescape).join(" ")
|
593
|
+
else
|
594
|
+
command
|
595
|
+
end
|
596
|
+
redirected = %Q{env #{env_args} #{c} > #{log_out.shellescape} 2>&1}
|
597
|
+
output "DEBUG: final command is #{redirected.inspect}" if opt_debug
|
388
598
|
system redirected
|
389
599
|
end
|
390
600
|
|
@@ -392,12 +602,12 @@ private
|
|
392
602
|
output "OK"
|
393
603
|
return true
|
394
604
|
else
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
output(
|
400
|
-
output("
|
605
|
+
output "ERROR. Please review logs to see what happened:\n"
|
606
|
+
[log_out, opt_altlog].compact.each do |log|
|
607
|
+
next unless File.exist?(log)
|
608
|
+
output("----- contents of '#{log}' -----")
|
609
|
+
output(File.read(log))
|
610
|
+
output("----- end of file -----")
|
401
611
|
end
|
402
612
|
raise "Failed to complete #{action} task"
|
403
613
|
end
|
@@ -448,7 +658,6 @@ private
|
|
448
658
|
def download_file_http(url, full_path, count = 3)
|
449
659
|
filename = File.basename(full_path)
|
450
660
|
with_tempfile(filename, full_path) do |temp_file|
|
451
|
-
progress = 0
|
452
661
|
total = 0
|
453
662
|
params = {
|
454
663
|
"Accept-Encoding" => 'identity',
|
@@ -457,12 +666,13 @@ private
|
|
457
666
|
if total
|
458
667
|
new_progress = (bytes * 100) / total
|
459
668
|
message "\rDownloading %s (%3d%%) " % [filename, new_progress]
|
460
|
-
progress = new_progress
|
461
669
|
else
|
462
670
|
# Content-Length is unavailable because Transfer-Encoding is chunked
|
463
671
|
message "\rDownloading %s " % [filename]
|
464
672
|
end
|
465
|
-
}
|
673
|
+
},
|
674
|
+
:open_timeout => @open_timeout,
|
675
|
+
:read_timeout => @read_timeout,
|
466
676
|
}
|
467
677
|
proxy_uri = URI.parse(url).scheme.downcase == 'https' ?
|
468
678
|
ENV["https_proxy"] :
|
@@ -487,7 +697,7 @@ private
|
|
487
697
|
return download_file(redirect.url, full_path, count-1)
|
488
698
|
rescue => e
|
489
699
|
count = count - 1
|
490
|
-
puts "#{count} retrie(s) left for #{filename}"
|
700
|
+
puts "#{count} retrie(s) left for #{filename} (#{e.message})"
|
491
701
|
if count > 0
|
492
702
|
sleep 1
|
493
703
|
return download_file_http(url, full_path, count)
|
@@ -505,17 +715,18 @@ private
|
|
505
715
|
end
|
506
716
|
|
507
717
|
def download_file_ftp(uri, full_path)
|
718
|
+
require "net/ftp"
|
508
719
|
filename = File.basename(uri.path)
|
509
720
|
with_tempfile(filename, full_path) do |temp_file|
|
510
|
-
progress = 0
|
511
721
|
total = 0
|
512
722
|
params = {
|
513
723
|
:content_length_proc => lambda{|length| total = length },
|
514
724
|
:progress_proc => lambda{|bytes|
|
515
725
|
new_progress = (bytes * 100) / total
|
516
726
|
message "\rDownloading %s (%3d%%) " % [filename, new_progress]
|
517
|
-
|
518
|
-
|
727
|
+
},
|
728
|
+
:open_timeout => @open_timeout,
|
729
|
+
:read_timeout => @read_timeout,
|
519
730
|
}
|
520
731
|
if ENV["ftp_proxy"]
|
521
732
|
_, userinfo, _p_host, _p_port = URI.split(ENV['ftp_proxy'])
|
@@ -530,6 +741,8 @@ private
|
|
530
741
|
end
|
531
742
|
output
|
532
743
|
end
|
744
|
+
rescue LoadError
|
745
|
+
raise LoadError, "Ruby #{RUBY_VERSION} does not provide the net-ftp gem, please add it as a dependency if you need to use FTP"
|
533
746
|
rescue Net::FTPError
|
534
747
|
return false
|
535
748
|
end
|
@@ -544,13 +757,28 @@ private
|
|
544
757
|
FileUtils.mv temp_file.path, full_path, :force => true
|
545
758
|
end
|
546
759
|
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
760
|
+
#
|
761
|
+
# this minimal version of pkg_config is based on ruby 29dc9378 (2023-01-09)
|
762
|
+
#
|
763
|
+
# specifically with the fix from b90e56e6 to support multiple pkg-config options, and removing
|
764
|
+
# code paths that aren't helpful for mini-portile's use case of parsing pc files.
|
765
|
+
#
|
766
|
+
def minimal_pkg_config(pkg, *pcoptions)
|
767
|
+
if pcoptions.empty?
|
768
|
+
raise ArgumentError, "no pkg-config options are given"
|
769
|
+
end
|
551
770
|
|
552
|
-
|
553
|
-
|
554
|
-
|
771
|
+
if ($PKGCONFIG ||=
|
772
|
+
(pkgconfig = MakeMakefile.with_config("pkg-config") {MakeMakefile.config_string("PKG_CONFIG") || "pkg-config"}) &&
|
773
|
+
MakeMakefile.find_executable0(pkgconfig) && pkgconfig)
|
774
|
+
pkgconfig = $PKGCONFIG
|
775
|
+
else
|
776
|
+
raise RuntimeError, "pkg-config is not found"
|
777
|
+
end
|
778
|
+
|
779
|
+
pcoptions = Array(pcoptions).map { |o| "--#{o}" }
|
780
|
+
response = IO.popen([pkgconfig, *pcoptions, pkg], err:[:child, :out], &:read)
|
781
|
+
raise RuntimeError, response unless $?.success?
|
782
|
+
response.strip
|
555
783
|
end
|
556
784
|
end
|
@@ -1,16 +1,24 @@
|
|
1
1
|
require 'mini_portile2/mini_portile'
|
2
|
+
require 'open3'
|
2
3
|
|
3
4
|
class MiniPortileCMake < MiniPortile
|
5
|
+
attr_accessor :system_name
|
6
|
+
|
4
7
|
def configure_prefix
|
5
8
|
"-DCMAKE_INSTALL_PREFIX=#{File.expand_path(port_path)}"
|
6
9
|
end
|
7
10
|
|
11
|
+
def initialize(name, version, **kwargs)
|
12
|
+
super(name, version, **kwargs)
|
13
|
+
@cmake_command = kwargs[:cmake_command]
|
14
|
+
@cmake_build_type = kwargs[:cmake_build_type]
|
15
|
+
end
|
16
|
+
|
8
17
|
def configure_defaults
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
18
|
+
[
|
19
|
+
generator_defaults,
|
20
|
+
cmake_compile_flags,
|
21
|
+
].flatten
|
14
22
|
end
|
15
23
|
|
16
24
|
def configure
|
@@ -19,7 +27,7 @@ class MiniPortileCMake < MiniPortile
|
|
19
27
|
cache_file = File.join(tmp_path, 'configure.options_cache')
|
20
28
|
File.open(cache_file, "w") { |f| f.write computed_options.to_s }
|
21
29
|
|
22
|
-
execute('configure',
|
30
|
+
execute('configure', [cmake_cmd] + computed_options + ["."])
|
23
31
|
end
|
24
32
|
|
25
33
|
def configured?
|
@@ -34,7 +42,103 @@ class MiniPortileCMake < MiniPortile
|
|
34
42
|
end
|
35
43
|
|
36
44
|
def make_cmd
|
37
|
-
return "nmake" if MiniPortile.
|
45
|
+
return "nmake" if MiniPortile.mswin?
|
38
46
|
super
|
39
47
|
end
|
48
|
+
|
49
|
+
def cmake_cmd
|
50
|
+
(ENV["CMAKE"] || @cmake_command || "cmake").dup
|
51
|
+
end
|
52
|
+
|
53
|
+
def cmake_build_type
|
54
|
+
(ENV["CMAKE_BUILD_TYPE"] || @cmake_build_type || "Release").dup
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def generator_defaults
|
60
|
+
if MiniPortile.mswin? && generator_available?('NMake')
|
61
|
+
['-G', 'NMake Makefiles']
|
62
|
+
elsif MiniPortile.mingw? && generator_available?('MSYS')
|
63
|
+
['-G', 'MSYS Makefiles']
|
64
|
+
else
|
65
|
+
[]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def cmake_compile_flags
|
70
|
+
c_compiler, cxx_compiler = find_c_and_cxx_compilers(host)
|
71
|
+
|
72
|
+
# needed to ensure cross-compilation with CMake targets the right CPU and compilers
|
73
|
+
[
|
74
|
+
"-DCMAKE_SYSTEM_NAME=#{cmake_system_name}",
|
75
|
+
"-DCMAKE_SYSTEM_PROCESSOR=#{cpu_type}",
|
76
|
+
"-DCMAKE_C_COMPILER=#{c_compiler}",
|
77
|
+
"-DCMAKE_CXX_COMPILER=#{cxx_compiler}",
|
78
|
+
"-DCMAKE_BUILD_TYPE=#{cmake_build_type}",
|
79
|
+
]
|
80
|
+
end
|
81
|
+
|
82
|
+
def find_compiler(compilers)
|
83
|
+
compilers.find { |binary| which(binary) }
|
84
|
+
end
|
85
|
+
|
86
|
+
# configure automatically searches for the right compiler based on the
|
87
|
+
# `--host` parameter. However, CMake doesn't have an equivalent feature.
|
88
|
+
# Search for the right compiler for the target architecture using
|
89
|
+
# some basic heruistics.
|
90
|
+
def find_c_and_cxx_compilers(host)
|
91
|
+
c_compiler = ENV["CC"]
|
92
|
+
cxx_compiler = ENV["CXX"]
|
93
|
+
|
94
|
+
if MiniPortile.darwin?
|
95
|
+
c_compiler ||= 'clang'
|
96
|
+
cxx_compiler ||='clang++'
|
97
|
+
else
|
98
|
+
c_compiler ||= 'gcc'
|
99
|
+
cxx_compiler ||= 'g++'
|
100
|
+
end
|
101
|
+
|
102
|
+
c_platform_compiler = "#{host}-#{c_compiler}"
|
103
|
+
cxx_platform_compiler = "#{host}-#{cxx_compiler}"
|
104
|
+
c_compiler = find_compiler([c_platform_compiler, c_compiler])
|
105
|
+
cxx_compiler = find_compiler([cxx_platform_compiler, cxx_compiler])
|
106
|
+
|
107
|
+
[c_compiler, cxx_compiler]
|
108
|
+
end
|
109
|
+
|
110
|
+
# Full list: https://gitlab.kitware.com/cmake/cmake/-/blob/v3.26.4/Modules/CMakeDetermineSystem.cmake?ref_type=tags#L12-31
|
111
|
+
def cmake_system_name
|
112
|
+
return system_name if system_name
|
113
|
+
|
114
|
+
if MiniPortile.linux?
|
115
|
+
'Linux'
|
116
|
+
elsif MiniPortile.darwin?
|
117
|
+
'Darwin'
|
118
|
+
elsif MiniPortile.windows?
|
119
|
+
'Windows'
|
120
|
+
elsif MiniPortile.freebsd?
|
121
|
+
'FreeBSD'
|
122
|
+
elsif MiniPortile.openbsd?
|
123
|
+
'OpenBSD'
|
124
|
+
elsif MiniPortile.solaris?
|
125
|
+
'SunOS'
|
126
|
+
else
|
127
|
+
raise "Unable to set CMAKE_SYSTEM_NAME for #{MiniPortile.target_os}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def generator_available?(generator_type)
|
132
|
+
stdout_str, status = Open3.capture2("#{cmake_cmd} --help")
|
133
|
+
|
134
|
+
raise 'Unable to determine whether CMake supports #{generator_type} Makefile generator' unless status.success?
|
135
|
+
|
136
|
+
stdout_str.include?("#{generator_type} Makefiles")
|
137
|
+
end
|
138
|
+
|
139
|
+
def cpu_type
|
140
|
+
return 'x86_64' if MiniPortile.target_cpu == 'x64'
|
141
|
+
|
142
|
+
MiniPortile.target_cpu
|
143
|
+
end
|
40
144
|
end
|