mini_portile2 2.0.0.rc1

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.
data/appveyor.yml ADDED
@@ -0,0 +1,24 @@
1
+ ---
2
+ install:
3
+ - ps: ((New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt', "$env:TMP\ca-bundle.crt"))
4
+ - SET SSL_CERT_FILE=%TMP%\ca-bundle.crt
5
+ - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
6
+ - SET RAKEOPT=-rdevkit
7
+ - ruby --version
8
+ - gem --version
9
+ - bundle install
10
+
11
+ build: off
12
+
13
+ test_script:
14
+ - bundle exec rake
15
+
16
+ environment:
17
+ matrix:
18
+ - ruby_version: "22-x64"
19
+ - ruby_version: "22"
20
+ - ruby_version: "21-x64"
21
+ - ruby_version: "21"
22
+ - ruby_version: "200-x64"
23
+ - ruby_version: "200"
24
+ - ruby_version: "193"
@@ -0,0 +1,2 @@
1
+ ports
2
+ tmp
data/examples/Rakefile ADDED
@@ -0,0 +1,127 @@
1
+ require 'rbconfig'
2
+
3
+ $: << File.expand_path(File.join(File.dirname(__FILE__), "../lib"))
4
+ require "mini_portile2"
5
+
6
+ recipes = []
7
+
8
+ def windows?
9
+ RbConfig::CONFIG['target_os'] =~ /mswin|mingw32/
10
+ end
11
+
12
+ # libiconv
13
+ libiconv = MiniPortile.new "libiconv", "1.14"
14
+ libiconv.files << "ftp://ftp.gnu.org/pub/gnu/#{libiconv.name}/#{libiconv.name}-#{libiconv.version}.tar.gz"
15
+ unless windows?
16
+ libiconv.patch_files = Dir["libiconv-patches/*.patch"].map { |dir| File.expand_path dir }
17
+ end
18
+
19
+ recipes.push libiconv
20
+
21
+ # sqlite3
22
+ sqlite3 = MiniPortile.new "sqlite3", "3.8.4.1"
23
+ sqlite3.files << "http://sqlite.org/2014/sqlite-autoconf-3080401.tar.gz"
24
+
25
+ recipes.push sqlite3
26
+
27
+ # c-ares
28
+ c_ares = MiniPortile.new "c-ares", "1.7.5"
29
+ c_ares.files << "http://distfiles.openknapsack.org/#{c_ares.name}/#{c_ares.name}-#{c_ares.version}.tar.gz"
30
+
31
+ recipes.push c_ares
32
+
33
+ # zlib
34
+ class ZlibRecipe < MiniPortile
35
+ def windows?
36
+ !(host =~ /mswin|mingw/).nil?
37
+ end
38
+
39
+ def configure
40
+ return super unless windows?
41
+
42
+ Dir.chdir work_path do
43
+ mk = File.read 'win32/Makefile.gcc'
44
+ File.open 'win32/Makefile.gcc', 'wb' do |f|
45
+ f.puts "BINARY_PATH = #{path}/bin"
46
+ f.puts "LIBRARY_PATH = #{path}/lib"
47
+ f.puts "INCLUDE_PATH = #{path}/include"
48
+
49
+ cross_build? and
50
+ mk.sub!(/^PREFIX\s*=\s*$/, "PREFIX = #{host}-")
51
+
52
+ f.puts mk
53
+ end
54
+ end
55
+ end
56
+
57
+ def configure_defaults
58
+ ["--static"]
59
+ end
60
+
61
+ def configured?
62
+ return super unless windows?
63
+
64
+ !!(File.read(File.join(work_path, 'win32/Makefile.gcc')) =~ /^BINARY_PATH/)
65
+ end
66
+
67
+ def compile
68
+ return super unless windows?
69
+
70
+ execute "compile", "make -f win32/Makefile.gcc"
71
+ end
72
+
73
+ def install
74
+ return if installed?
75
+ return super unless windows?
76
+
77
+ execute "install", %Q(#{make_cmd} -f win32/Makefile.gcc install)
78
+ end
79
+
80
+ def cross_build?
81
+ host != original_host
82
+ end
83
+ end
84
+
85
+ zlib = ZlibRecipe.new "zlib", "1.2.8"
86
+ zlib.files << {
87
+ url: "http://zlib.net/#{zlib.name}-#{zlib.version}.tar.gz",
88
+ md5: "44d667c142d7cda120332623eab69f40",
89
+ }
90
+
91
+
92
+ recipes.push zlib
93
+
94
+ namespace :ports do
95
+ directory "ports"
96
+
97
+ recipes.each do |recipe|
98
+ desc "Install port #{recipe.name} #{recipe.version}"
99
+ task recipe.name => ["ports"] do |t|
100
+ checkpoint = "ports/.#{recipe.name}-#{recipe.version}-#{recipe.host}.installed"
101
+
102
+ unless File.exist?(checkpoint)
103
+ recipe.cook
104
+ touch checkpoint
105
+ end
106
+
107
+ recipe.activate
108
+ end
109
+
110
+ task :all => recipe.name
111
+ end
112
+
113
+ desc "Install all ports and display installation location"
114
+ task :all do
115
+ recipes.each do |recipe|
116
+ puts "Artifacts of '#{recipe.name}' in '#{recipe.path}'"
117
+ end
118
+ puts "LDFLAGS: " << ENV['LDFLAGS'].inspect
119
+ end
120
+ end
121
+
122
+ desc "Adjust all recipes host for cross-compilation"
123
+ task :cross do
124
+ recipes.each do |recipe|
125
+ recipe.host = "i686-w64-mingw32"
126
+ end
127
+ end
@@ -0,0 +1,16 @@
1
+ This file tests the 'patch' functionality, as well as working around a
2
+ libiconv compilation issue with glibc >= 2.16.
3
+
4
+ --- a/srclib/stdio.in.h 2015-08-23 13:59:57.395880263 -0400
5
+ +++ b/srclib/stdio.in.h 2015-08-23 14:00:00.047880153 -0400
6
+ @@ -695,8 +695,10 @@
7
+ /* It is very rare that the developer ever has full control of stdin,
8
+ so any use of gets warrants an unconditional warning. Assume it is
9
+ always declared, since it is required by C89. */
10
+ +#if defined(__GLIBC__) && !defined(__UCLIBC__) && !__GLIBC_PREREQ(2, 16)
11
+ _GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");
12
+ #endif
13
+ +#endif
14
+
15
+
16
+ #if @GNULIB_OBSTACK_PRINTF@ || @GNULIB_OBSTACK_PRINTF_POSIX@
@@ -0,0 +1,2 @@
1
+ require "mini_portile2/version"
2
+ require "mini_portile2/mini_portile"
@@ -0,0 +1,495 @@
1
+ require 'rbconfig'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'net/ftp'
5
+ require 'fileutils'
6
+ require 'tempfile'
7
+ require 'digest/md5'
8
+ require 'open-uri'
9
+ require 'cgi'
10
+ require 'rbconfig'
11
+ require 'shellwords'
12
+
13
+ # Monkey patch for Net::HTTP by ruby open-uri fix:
14
+ # https://github.com/ruby/ruby/commit/58835a9
15
+ class Net::HTTP
16
+ private
17
+ remove_method(:edit_path)
18
+ def edit_path(path)
19
+ if proxy?
20
+ if path.start_with?("ftp://") || use_ssl?
21
+ path
22
+ else
23
+ "http://#{addr_port}#{path}"
24
+ end
25
+ else
26
+ path
27
+ end
28
+ end
29
+ end
30
+
31
+ class MiniPortile
32
+ attr_reader :name, :version, :original_host
33
+ attr_writer :configure_options
34
+ attr_accessor :host, :files, :patch_files, :target, :logger
35
+
36
+ def initialize(name, version)
37
+ @name = name
38
+ @version = version
39
+ @target = 'ports'
40
+ @files = []
41
+ @patch_files = []
42
+ @log_files = {}
43
+ @logger = STDOUT
44
+
45
+ @original_host = @host = detect_host
46
+ end
47
+
48
+ def download
49
+ files_hashs.each do |file|
50
+ download_file(file[:url], file[:local_path])
51
+ verify_file(file)
52
+ end
53
+ end
54
+
55
+ def extract
56
+ files_hashs.each do |file|
57
+ extract_file(file[:local_path], tmp_path)
58
+ end
59
+ end
60
+
61
+ def apply_patch(patch_file)
62
+ (
63
+ # Not a class variable because closures will capture self.
64
+ @apply_patch ||=
65
+ case
66
+ when which('git')
67
+ lambda { |file|
68
+ 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", file], :initial_message => false)
72
+ }
73
+ when which('patch')
74
+ lambda { |file|
75
+ message "Running patch with #{file}... "
76
+ execute('patch', ["patch", "-p1", "-i", file], :initial_message => false)
77
+ }
78
+ else
79
+ raise "Failed to complete patch task; patch(1) or git(1) is required."
80
+ end
81
+ ).call(patch_file)
82
+ end
83
+
84
+ def patch
85
+ @patch_files.each do |full_path|
86
+ next unless File.exist?(full_path)
87
+ apply_patch(full_path)
88
+ end
89
+ end
90
+
91
+ def configure_options
92
+ @configure_options ||= configure_defaults
93
+ end
94
+
95
+ def configure
96
+ return if configured?
97
+
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 }
101
+
102
+ execute('configure', %w(sh configure) + computed_options)
103
+ end
104
+
105
+ def compile
106
+ execute('compile', make_cmd)
107
+ end
108
+
109
+ def install
110
+ return if installed?
111
+ execute('install', %Q(#{make_cmd} install))
112
+ end
113
+
114
+ def downloaded?
115
+ missing = files_hashs.detect do |file|
116
+ !File.exist?(file[:local_path])
117
+ end
118
+
119
+ missing ? false : true
120
+ end
121
+
122
+ def configured?
123
+ configure = File.join(work_path, 'configure')
124
+ makefile = File.join(work_path, 'Makefile')
125
+ md5_file = File.join(tmp_path, 'configure.md5')
126
+
127
+ stored_md5 = File.exist?(md5_file) ? File.read(md5_file) : ""
128
+ current_md5 = Digest::MD5.hexdigest(computed_options.to_s)
129
+
130
+ (current_md5 == stored_md5) && newer?(makefile, configure)
131
+ end
132
+
133
+ def installed?
134
+ makefile = File.join(work_path, 'Makefile')
135
+ target_dir = Dir.glob("#{port_path}/*").find { |d| File.directory?(d) }
136
+
137
+ newer?(target_dir, makefile)
138
+ end
139
+
140
+ def cook
141
+ download unless downloaded?
142
+ extract
143
+ patch
144
+ configure unless configured?
145
+ compile
146
+ install unless installed?
147
+
148
+ return true
149
+ end
150
+
151
+ def activate
152
+ lib_path = File.join(port_path, "lib")
153
+ vars = {
154
+ 'PATH' => File.join(port_path, 'bin'),
155
+ 'CPATH' => File.join(port_path, 'include'),
156
+ 'LIBRARY_PATH' => lib_path
157
+ }.reject { |env, path| !File.directory?(path) }
158
+
159
+ output "Activating #{@name} #{@version} (from #{port_path})..."
160
+ vars.each do |var, path|
161
+ full_path = File.expand_path(path)
162
+
163
+ # turn into a valid Windows path (if required)
164
+ full_path.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
165
+
166
+ # save current variable value
167
+ old_value = ENV[var] || ''
168
+
169
+ unless old_value.include?(full_path)
170
+ ENV[var] = "#{full_path}#{File::PATH_SEPARATOR}#{old_value}"
171
+ end
172
+ end
173
+
174
+ # rely on LDFLAGS when cross-compiling
175
+ if File.exist?(lib_path) && (@host != @original_host)
176
+ full_path = File.expand_path(lib_path)
177
+
178
+ old_value = ENV.fetch("LDFLAGS", "")
179
+
180
+ unless old_value.include?(full_path)
181
+ ENV["LDFLAGS"] = "-L#{full_path} #{old_value}".strip
182
+ end
183
+ end
184
+ end
185
+
186
+ def path
187
+ File.expand_path(port_path)
188
+ end
189
+
190
+ private
191
+
192
+ def tmp_path
193
+ "tmp/#{@host}/ports/#{@name}/#{@version}"
194
+ end
195
+
196
+ def port_path
197
+ "#{@target}/#{@host}/#{@name}/#{@version}"
198
+ end
199
+
200
+ def archives_path
201
+ "#{@target}/archives"
202
+ end
203
+
204
+ def work_path
205
+ Dir.glob("#{tmp_path}/*").find { |d| File.directory?(d) }
206
+ end
207
+
208
+ def configure_defaults
209
+ [
210
+ "--host=#{@host}", # build for specific target (host)
211
+ "--enable-static", # build static library
212
+ "--disable-shared" # disable generation of shared object
213
+ ]
214
+ end
215
+
216
+ def configure_prefix
217
+ "--prefix=#{File.expand_path(port_path)}"
218
+ end
219
+
220
+ def computed_options
221
+ [
222
+ configure_options, # customized or default options
223
+ configure_prefix, # installation target
224
+ ].flatten
225
+ end
226
+
227
+ def files_hashs
228
+ @files.map do |file|
229
+ hash = case file
230
+ when String
231
+ { :url => file }
232
+ when Hash
233
+ file.dup
234
+ else
235
+ raise ArgumentError, "files must be an Array of Stings or Hashs"
236
+ end
237
+
238
+ url = hash.fetch(:url){ raise ArgumentError, "no url given" }
239
+ filename = File.basename(url)
240
+ hash[:local_path] = File.join(archives_path, filename)
241
+ hash
242
+ end
243
+ end
244
+
245
+ def verify_file(file)
246
+ digest = case
247
+ when exp=file[:sha256] then Digest::SHA256
248
+ when exp=file[:sha1] then Digest::SHA1
249
+ when exp=file[:md5] then Digest::MD5
250
+ end
251
+ if digest
252
+ is = digest.file(file[:local_path]).hexdigest
253
+ unless is == exp.downcase
254
+ raise "Downloaded file '#{file[:local_path]}' has wrong hash: expected: #{exp} is: #{is}"
255
+ end
256
+ end
257
+ end
258
+
259
+ def log_file(action)
260
+ @log_files[action] ||=
261
+ File.expand_path("#{action}.log", tmp_path).tap { |file|
262
+ File.unlink(file) if File.exist?(file)
263
+ }
264
+ end
265
+
266
+ def tar_exe
267
+ @@tar_exe ||= begin
268
+ %w[gtar bsdtar tar basic-bsdtar].find { |c|
269
+ which(c)
270
+ }
271
+ end
272
+ end
273
+
274
+ def tar_compression_switch(filename)
275
+ case File.extname(filename)
276
+ when '.gz', '.tgz'
277
+ 'z'
278
+ when '.bz2', '.tbz2'
279
+ 'j'
280
+ when '.Z'
281
+ 'Z'
282
+ else
283
+ ''
284
+ end
285
+ end
286
+
287
+ # From: http://stackoverflow.com/a/5471032/7672
288
+ # Thanks, Mislav!
289
+ #
290
+ # Cross-platform way of finding an executable in the $PATH.
291
+ #
292
+ # which('ruby') #=> /usr/bin/ruby
293
+ def which(cmd)
294
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
295
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
296
+ exts.each { |ext|
297
+ exe = File.join(path, "#{cmd}#{ext}")
298
+ return exe if File.executable? exe
299
+ }
300
+ end
301
+ return nil
302
+ end
303
+
304
+ def detect_host
305
+ return @detect_host if defined?(@detect_host)
306
+
307
+ begin
308
+ ENV["LC_ALL"], old_lc_all = "C", ENV["LC_ALL"]
309
+
310
+ output = `#{gcc_cmd} -v 2>&1`
311
+ if m = output.match(/^Target\: (.*)$/)
312
+ @detect_host = m[1]
313
+ end
314
+
315
+ @detect_host
316
+ ensure
317
+ ENV["LC_ALL"] = old_lc_all
318
+ end
319
+ end
320
+
321
+ def extract_file(file, target)
322
+ filename = File.basename(file)
323
+ FileUtils.mkdir_p target
324
+
325
+ message "Extracting #{filename} into #{target}... "
326
+ execute('extract', [tar_exe, "#{tar_compression_switch(filename)}xf", file, "-C", target], {:cd => Dir.pwd, :initial_message => false})
327
+ end
328
+
329
+ def execute(action, command, options={})
330
+ log_out = log_file(action)
331
+
332
+ Dir.chdir (options.fetch(:cd){ work_path }) do
333
+ if options.fetch(:initial_message){ true }
334
+ message "Running '#{action}' for #{@name} #{@version}... "
335
+ end
336
+
337
+ if Process.respond_to?(:spawn) && ! RbConfig.respond_to?(:java)
338
+ args = [command].flatten + [{[:out, :err]=>[log_out, "a"]}]
339
+ pid = spawn(*args)
340
+ Process.wait(pid)
341
+ else
342
+ redirected = if command.kind_of?(Array)
343
+ %Q{#{command.map(&:shellescape).join(" ")} > #{log_out.shellescape} 2>&1}
344
+ else
345
+ %Q{#{command} > #{log_out.shellescape} 2>&1}
346
+ end
347
+ system redirected
348
+ end
349
+
350
+ if $?.success?
351
+ output "OK"
352
+ return true
353
+ else
354
+ if File.exist? log_out
355
+ output "ERROR, review '#{log_out}' to see what happened. Last lines are:"
356
+ output("=" * 72)
357
+ log_lines = File.readlines(log_out)
358
+ output(log_lines[-[log_lines.length, 20].min .. -1])
359
+ output("=" * 72)
360
+ end
361
+ raise "Failed to complete #{action} task"
362
+ end
363
+ end
364
+ end
365
+
366
+ def newer?(target, checkpoint)
367
+ if (target && File.exist?(target)) && (checkpoint && File.exist?(checkpoint))
368
+ File.mtime(target) > File.mtime(checkpoint)
369
+ else
370
+ false
371
+ end
372
+ end
373
+
374
+ # print out a message with the logger
375
+ def message(text)
376
+ @logger.print text
377
+ @logger.flush
378
+ end
379
+
380
+ # print out a message using the logger but return to a new line
381
+ def output(text = "")
382
+ @logger.puts text
383
+ @logger.flush
384
+ end
385
+
386
+ # Slighly modified from RubyInstaller uri_ext, Rubinius configure
387
+ # and adaptations of Wayne's RailsInstaller
388
+ def download_file(url, full_path, count = 3)
389
+ return if File.exist?(full_path)
390
+ uri = URI.parse(url)
391
+ begin
392
+ case uri.scheme.downcase
393
+ when /ftp/
394
+ download_file_ftp(uri, full_path)
395
+ when /http|https/
396
+ download_file_http(url, full_path, count)
397
+ end
398
+ rescue Exception => e
399
+ File.unlink full_path if File.exist?(full_path)
400
+ output "ERROR: #{e.message}"
401
+ raise "Failed to complete download task"
402
+ end
403
+ end
404
+
405
+ def download_file_http(url, full_path, count = 3)
406
+ filename = File.basename(full_path)
407
+ with_tempfile(filename, full_path) do |temp_file|
408
+ progress = 0
409
+ total = 0
410
+ params = {
411
+ "Accept-Encoding" => 'identity',
412
+ :content_length_proc => lambda{|length| total = length },
413
+ :progress_proc => lambda{|bytes|
414
+ new_progress = (bytes * 100) / total
415
+ message "\rDownloading %s (%3d%%) " % [filename, new_progress]
416
+ progress = new_progress
417
+ }
418
+ }
419
+ proxy_uri = URI.parse(url).scheme.downcase == 'https' ?
420
+ ENV["https_proxy"] :
421
+ ENV["http_proxy"]
422
+ if proxy_uri
423
+ _, userinfo, _p_host, _p_port = URI.split(proxy_uri)
424
+ if userinfo
425
+ proxy_user, proxy_pass = userinfo.split(/:/).map{|s| CGI.unescape(s) }
426
+ params[:proxy_http_basic_authentication] =
427
+ [proxy_uri, proxy_user, proxy_pass]
428
+ end
429
+ end
430
+ begin
431
+ OpenURI.open_uri(url, 'rb', params) do |io|
432
+ temp_file << io.read
433
+ end
434
+ output
435
+ rescue OpenURI::HTTPRedirect => redirect
436
+ raise "Too many redirections for the original URL, halting." if count <= 0
437
+ count = count - 1
438
+ return download_file(redirect.url, full_path, count - 1)
439
+ rescue => e
440
+ output e.message
441
+ return false
442
+ end
443
+ end
444
+ end
445
+
446
+ def download_file_ftp(uri, full_path)
447
+ filename = File.basename(uri.path)
448
+ with_tempfile(filename, full_path) do |temp_file|
449
+ progress = 0
450
+ total = 0
451
+ params = {
452
+ :content_length_proc => lambda{|length| total = length },
453
+ :progress_proc => lambda{|bytes|
454
+ new_progress = (bytes * 100) / total
455
+ message "\rDownloading %s (%3d%%) " % [filename, new_progress]
456
+ progress = new_progress
457
+ }
458
+ }
459
+ if ENV["ftp_proxy"]
460
+ _, userinfo, _p_host, _p_port = URI.split(ENV['ftp_proxy'])
461
+ if userinfo
462
+ proxy_user, proxy_pass = userinfo.split(/:/).map{|s| CGI.unescape(s) }
463
+ params[:proxy_http_basic_authentication] =
464
+ [ENV['ftp_proxy'], proxy_user, proxy_pass]
465
+ end
466
+ end
467
+ OpenURI.open_uri(uri, 'rb', params) do |io|
468
+ temp_file << io.read
469
+ end
470
+ output
471
+ end
472
+ rescue Net::FTPError
473
+ return false
474
+ end
475
+
476
+ def with_tempfile(filename, full_path)
477
+ temp_file = Tempfile.new("download-#{filename}")
478
+ temp_file.binmode
479
+ yield temp_file
480
+ temp_file.close
481
+ File.unlink full_path if File.exist?(full_path)
482
+ FileUtils.mkdir_p File.dirname(full_path)
483
+ FileUtils.mv temp_file.path, full_path, :force => true
484
+ end
485
+
486
+ def gcc_cmd
487
+ cc = ENV["CC"] || RbConfig::CONFIG["CC"] || "gcc"
488
+ return cc.dup
489
+ end
490
+
491
+ def make_cmd
492
+ m = ENV['MAKE'] || ENV['make'] || 'make'
493
+ return m.dup
494
+ end
495
+ end