fiddle 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +52 -1
- data/Rakefile +3 -5
- data/bin/downloader.rb +331 -0
- data/bin/extlibs.rb +262 -0
- data/ext/fiddle/closure.c +5 -4
- data/ext/fiddle/conversions.c +161 -16
- data/ext/fiddle/conversions.h +14 -4
- data/ext/fiddle/depend +83 -0
- data/ext/fiddle/extconf.rb +54 -28
- data/ext/fiddle/extlibs +10 -2
- data/ext/fiddle/fiddle.c +46 -35
- data/ext/fiddle/fiddle.h +35 -1
- data/ext/fiddle/function.c +220 -81
- data/ext/fiddle/handle.c +10 -12
- data/ext/fiddle/pinned.c +123 -0
- data/ext/fiddle/pointer.c +106 -24
- data/ext/fiddle/win32/fficonfig.h +0 -0
- data/ext/fiddle/win32/libffi-config.rb +1 -1
- data/ext/fiddle/win32/libffi.mk.tmpl +0 -0
- data/fiddle.gemspec +46 -4
- data/lib/fiddle.rb +3 -1
- data/lib/fiddle/cparser.rb +29 -2
- data/lib/fiddle/import.rb +11 -9
- data/lib/fiddle/pack.rb +14 -7
- data/lib/fiddle/struct.rb +267 -43
- data/lib/fiddle/value.rb +18 -9
- data/lib/fiddle/version.rb +3 -0
- metadata +13 -12
- data/.gitignore +0 -13
- data/.travis.yml +0 -7
- data/Gemfile +0 -4
- data/bin/console +0 -14
- data/bin/setup +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6273f6abe03da289f77f31e82b7d43fcb99e7144f096319306e91e6d0bc33659
|
4
|
+
data.tar.gz: de279d21f060c1726c701adc9dba5518bb85b179d3ac4dfd716b995948147163
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7afc807eaa5b363d2687027ec7c4d2fffc7779286359095e96a266feb7cdbb0bf6addd8b58b5cb41cff9f1ef86764381828e172b63017cc7567d237f46c9c502
|
7
|
+
data.tar.gz: 8d4cb60138cabdff92524750189ae8024faef07e16d21b9b2b10b7a7bfc7e04913bd61dd1f8482feacbb50b5b9a6f2a4e0f2515f62a6a9c32e6bb3f9ff631bc6
|
data/README.md
CHANGED
@@ -28,7 +28,7 @@ Or install it yourself as:
|
|
28
28
|
|
29
29
|
Here we will use Fiddle::Function to wrap [floor(3) from libm](http://linux.die.net/man/3/floor)
|
30
30
|
|
31
|
-
```
|
31
|
+
```ruby
|
32
32
|
require 'fiddle'
|
33
33
|
|
34
34
|
libm = Fiddle.dlopen('/lib/libm.so.6')
|
@@ -42,6 +42,57 @@ floor = Fiddle::Function.new(
|
|
42
42
|
puts floor.call(3.14159) #=> 3.0
|
43
43
|
```
|
44
44
|
|
45
|
+
### Nested Structs
|
46
|
+
|
47
|
+
You can use hashes to create nested structs, where the hash keys are member names and the values are the nested structs:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
StudentCollegeDetail = struct [
|
51
|
+
'int college_id',
|
52
|
+
'char college_name[50]'
|
53
|
+
]
|
54
|
+
|
55
|
+
StudentDetail = struct [
|
56
|
+
'int id',
|
57
|
+
'char name[20]',
|
58
|
+
{ clg_data: StudentCollegeDetail }
|
59
|
+
]
|
60
|
+
```
|
61
|
+
|
62
|
+
You can also specify an anonymous nested struct, like so:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
StudentDetail = struct [
|
66
|
+
'int id',
|
67
|
+
'char name[20]',
|
68
|
+
{
|
69
|
+
clg_data: struct([
|
70
|
+
'int college_id',
|
71
|
+
'char college_name[50]'
|
72
|
+
])
|
73
|
+
}
|
74
|
+
]
|
75
|
+
```
|
76
|
+
|
77
|
+
The position of a hash (and the order of the keys in the hash, in the case of a hash with multiple entries), dictate the offsets of the nested struct in memory. The following examples are both syntactically valid but will lay out the structs differently in memory:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
# order of members in memory: position, id, dimensions
|
81
|
+
Rect = struct [ { position: struct(['float x', 'float y']) },
|
82
|
+
'int id',
|
83
|
+
{ dimensions: struct(['float w', 'float h']) }
|
84
|
+
]
|
85
|
+
|
86
|
+
# order of members in memory: id, position, dimensions
|
87
|
+
Rect = struct [ 'int id',
|
88
|
+
{
|
89
|
+
position: struct(['float x', 'float y']),
|
90
|
+
dimensions: struct(['float w', 'float h'])
|
91
|
+
}
|
92
|
+
]
|
93
|
+
```
|
94
|
+
|
95
|
+
|
45
96
|
## Development
|
46
97
|
|
47
98
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/Rakefile
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
-
require "rake/testtask"
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
t.test_files = FileList['test/**/test_*.rb']
|
3
|
+
desc "Run test"
|
4
|
+
task :test do
|
5
|
+
ruby("test/run.rb")
|
8
6
|
end
|
9
7
|
|
10
8
|
require 'rake/extensiontask'
|
data/bin/downloader.rb
ADDED
@@ -0,0 +1,331 @@
|
|
1
|
+
# Used by configure and make to download or update mirrored Ruby and GCC
|
2
|
+
# files. This will use HTTPS if possible, falling back to HTTP.
|
3
|
+
|
4
|
+
require 'fileutils'
|
5
|
+
require 'open-uri'
|
6
|
+
require 'pathname'
|
7
|
+
begin
|
8
|
+
require 'net/https'
|
9
|
+
rescue LoadError
|
10
|
+
https = 'http'
|
11
|
+
else
|
12
|
+
https = 'https'
|
13
|
+
|
14
|
+
# open-uri of ruby 2.2.0 accept an array of PEMs as ssl_ca_cert, but old
|
15
|
+
# versions are not. so, patching OpenSSL::X509::Store#add_file instead.
|
16
|
+
class OpenSSL::X509::Store
|
17
|
+
alias orig_add_file add_file
|
18
|
+
def add_file(pems)
|
19
|
+
Array(pems).each do |pem|
|
20
|
+
if File.directory?(pem)
|
21
|
+
add_path pem
|
22
|
+
else
|
23
|
+
orig_add_file pem
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# since open-uri internally checks ssl_ca_cert using File.directory?,
|
29
|
+
# allow to accept an array.
|
30
|
+
class <<File
|
31
|
+
alias orig_directory? directory?
|
32
|
+
def File.directory? files
|
33
|
+
files.is_a?(Array) ? false : orig_directory?(files)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Downloader
|
39
|
+
def self.https=(https)
|
40
|
+
@@https = https
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.https?
|
44
|
+
@@https == 'https'
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.https
|
48
|
+
@@https
|
49
|
+
end
|
50
|
+
|
51
|
+
class GNU < self
|
52
|
+
def self.download(name, *rest)
|
53
|
+
if https?
|
54
|
+
super("https://raw.githubusercontent.com/gcc-mirror/gcc/master/#{name}", name, *rest)
|
55
|
+
else
|
56
|
+
super("https://repo.or.cz/official-gcc.git/blob_plain/HEAD:/#{name}", name, *rest)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class RubyGems < self
|
62
|
+
def self.download(name, dir = nil, since = true, options = {})
|
63
|
+
require 'rubygems'
|
64
|
+
options = options.dup
|
65
|
+
options[:ssl_ca_cert] = Dir.glob(File.expand_path("../lib/rubygems/ssl_certs/**/*.pem", File.dirname(__FILE__)))
|
66
|
+
super("https://rubygems.org/downloads/#{name}", name, dir, since, options)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
Gems = RubyGems
|
71
|
+
|
72
|
+
class Unicode < self
|
73
|
+
def self.download(name, *rest)
|
74
|
+
super("http://www.unicode.org/Public/#{name}", name, *rest)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.mode_for(data)
|
79
|
+
/\A#!/ =~ data ? 0755 : 0644
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.http_options(file, since)
|
83
|
+
options = {}
|
84
|
+
if since
|
85
|
+
case since
|
86
|
+
when true
|
87
|
+
since = (File.mtime(file).httpdate rescue nil)
|
88
|
+
when Time
|
89
|
+
since = since.httpdate
|
90
|
+
end
|
91
|
+
if since
|
92
|
+
options['If-Modified-Since'] = since
|
93
|
+
end
|
94
|
+
end
|
95
|
+
options['Accept-Encoding'] = '*' # to disable Net::HTTP::GenericRequest#decode_content
|
96
|
+
options
|
97
|
+
end
|
98
|
+
|
99
|
+
# Downloader.download(url, name, [dir, [since]])
|
100
|
+
#
|
101
|
+
# Update a file from url if newer version is available.
|
102
|
+
# Creates the file if the file doesn't yet exist; however, the
|
103
|
+
# directory where the file is being created has to exist already.
|
104
|
+
# The +since+ parameter can take the following values, with associated meanings:
|
105
|
+
# true ::
|
106
|
+
# Take the last-modified time of the current file on disk, and only download
|
107
|
+
# if the server has a file that was modified later. Download unconditionally
|
108
|
+
# if we don't have the file yet. Default.
|
109
|
+
# +some time value+ ::
|
110
|
+
# Use this time value instead of the time of modification of the file on disk.
|
111
|
+
# nil ::
|
112
|
+
# Only download the file if it doesn't exist yet.
|
113
|
+
# false ::
|
114
|
+
# always download url regardless of whether we already have a file,
|
115
|
+
# and regardless of modification times. (This is essentially just a waste of
|
116
|
+
# network resources, except in the case that the file we have is somehow damaged.
|
117
|
+
# Please note that using this recurringly might create or be seen as a
|
118
|
+
# denial of service attack.)
|
119
|
+
#
|
120
|
+
# Example usage:
|
121
|
+
# download 'http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt',
|
122
|
+
# 'UnicodeData.txt', 'enc/unicode/data'
|
123
|
+
def self.download(url, name, dir = nil, since = true, options = {})
|
124
|
+
options = options.dup
|
125
|
+
url = URI(url)
|
126
|
+
dryrun = options.delete(:dryrun)
|
127
|
+
if name
|
128
|
+
file = Pathname.new(under(dir, name))
|
129
|
+
else
|
130
|
+
name = File.basename(url.path)
|
131
|
+
end
|
132
|
+
cache_save = options.delete(:cache_save) {
|
133
|
+
ENV["CACHE_SAVE"] != "no"
|
134
|
+
}
|
135
|
+
cache = cache_file(url, name, options.delete(:cache_dir))
|
136
|
+
file ||= cache
|
137
|
+
if since.nil? and file.exist?
|
138
|
+
if $VERBOSE
|
139
|
+
$stdout.puts "#{file} already exists"
|
140
|
+
$stdout.flush
|
141
|
+
end
|
142
|
+
if cache_save
|
143
|
+
save_cache(cache, file, name)
|
144
|
+
end
|
145
|
+
return file.to_path
|
146
|
+
end
|
147
|
+
if dryrun
|
148
|
+
puts "Download #{url} into #{file}"
|
149
|
+
return
|
150
|
+
end
|
151
|
+
if link_cache(cache, file, name, $VERBOSE)
|
152
|
+
return file.to_path
|
153
|
+
end
|
154
|
+
if !https? and URI::HTTPS === url
|
155
|
+
warn "*** using http instead of https ***"
|
156
|
+
url.scheme = 'http'
|
157
|
+
url = URI(url.to_s)
|
158
|
+
end
|
159
|
+
if $VERBOSE
|
160
|
+
$stdout.print "downloading #{name} ... "
|
161
|
+
$stdout.flush
|
162
|
+
end
|
163
|
+
begin
|
164
|
+
data = url.read(options.merge(http_options(file, since.nil? ? true : since)))
|
165
|
+
rescue OpenURI::HTTPError => http_error
|
166
|
+
if http_error.message =~ /^304 / # 304 Not Modified
|
167
|
+
if $VERBOSE
|
168
|
+
$stdout.puts "#{name} not modified"
|
169
|
+
$stdout.flush
|
170
|
+
end
|
171
|
+
return file.to_path
|
172
|
+
end
|
173
|
+
raise
|
174
|
+
rescue Timeout::Error
|
175
|
+
if since.nil? and file.exist?
|
176
|
+
puts "Request for #{url} timed out, using old version."
|
177
|
+
return file.to_path
|
178
|
+
end
|
179
|
+
raise
|
180
|
+
rescue SocketError
|
181
|
+
if since.nil? and file.exist?
|
182
|
+
puts "No network connection, unable to download #{url}, using old version."
|
183
|
+
return file.to_path
|
184
|
+
end
|
185
|
+
raise
|
186
|
+
end
|
187
|
+
mtime = nil
|
188
|
+
dest = (cache_save && cache && !cache.exist? ? cache : file)
|
189
|
+
dest.parent.mkpath
|
190
|
+
dest.open("wb", 0600) do |f|
|
191
|
+
f.write(data)
|
192
|
+
f.chmod(mode_for(data))
|
193
|
+
mtime = data.meta["last-modified"]
|
194
|
+
end
|
195
|
+
if mtime
|
196
|
+
mtime = Time.httpdate(mtime)
|
197
|
+
dest.utime(mtime, mtime)
|
198
|
+
end
|
199
|
+
if $VERBOSE
|
200
|
+
$stdout.puts "done"
|
201
|
+
$stdout.flush
|
202
|
+
end
|
203
|
+
if dest.eql?(cache)
|
204
|
+
link_cache(cache, file, name)
|
205
|
+
elsif cache_save
|
206
|
+
save_cache(cache, file, name)
|
207
|
+
end
|
208
|
+
return file.to_path
|
209
|
+
rescue => e
|
210
|
+
raise "failed to download #{name}\n#{e.message}: #{url}"
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.under(dir, name)
|
214
|
+
dir ? File.join(dir, File.basename(name)) : name
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.cache_file(url, name, cache_dir = nil)
|
218
|
+
case cache_dir
|
219
|
+
when false
|
220
|
+
return nil
|
221
|
+
when nil
|
222
|
+
cache_dir = ENV['CACHE_DIR']
|
223
|
+
if !cache_dir or cache_dir.empty?
|
224
|
+
cache_dir = ".downloaded-cache"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
Pathname.new(cache_dir) + (name || File.basename(URI(url).path))
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.link_cache(cache, file, name, verbose = false)
|
231
|
+
return false unless cache and cache.exist?
|
232
|
+
return true if cache.eql?(file)
|
233
|
+
if /cygwin/ !~ RUBY_PLATFORM or /winsymlink:nativestrict/ =~ ENV['CYGWIN']
|
234
|
+
begin
|
235
|
+
file.make_symlink(cache.relative_path_from(file.parent))
|
236
|
+
rescue SystemCallError
|
237
|
+
else
|
238
|
+
if verbose
|
239
|
+
$stdout.puts "made symlink #{name} to #{cache}"
|
240
|
+
$stdout.flush
|
241
|
+
end
|
242
|
+
return true
|
243
|
+
end
|
244
|
+
end
|
245
|
+
begin
|
246
|
+
file.make_link(cache)
|
247
|
+
rescue SystemCallError
|
248
|
+
else
|
249
|
+
if verbose
|
250
|
+
$stdout.puts "made link #{name} to #{cache}"
|
251
|
+
$stdout.flush
|
252
|
+
end
|
253
|
+
return true
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def self.save_cache(cache, file, name)
|
258
|
+
if cache and !cache.eql?(file) and !cache.exist?
|
259
|
+
begin
|
260
|
+
file.rename(cache)
|
261
|
+
rescue
|
262
|
+
else
|
263
|
+
link_cache(cache, file, name)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
Downloader.https = https.freeze
|
270
|
+
|
271
|
+
if $0 == __FILE__
|
272
|
+
since = true
|
273
|
+
options = {}
|
274
|
+
until ARGV.empty?
|
275
|
+
case ARGV[0]
|
276
|
+
when '-d'
|
277
|
+
destdir = ARGV[1]
|
278
|
+
ARGV.shift
|
279
|
+
when '-p'
|
280
|
+
# strip directory names from the name to download, and add the
|
281
|
+
# prefix instead.
|
282
|
+
prefix = ARGV[1]
|
283
|
+
ARGV.shift
|
284
|
+
when '-e'
|
285
|
+
since = nil
|
286
|
+
when '-a'
|
287
|
+
since = false
|
288
|
+
when '-n', '--dryrun'
|
289
|
+
options[:dryrun] = true
|
290
|
+
when '--cache-dir'
|
291
|
+
options[:cache_dir] = ARGV[1]
|
292
|
+
ARGV.shift
|
293
|
+
when /\A--cache-dir=(.*)/m
|
294
|
+
options[:cache_dir] = $1
|
295
|
+
when /\A-/
|
296
|
+
abort "#{$0}: unknown option #{ARGV[0]}"
|
297
|
+
else
|
298
|
+
break
|
299
|
+
end
|
300
|
+
ARGV.shift
|
301
|
+
end
|
302
|
+
dl = Downloader.constants.find do |name|
|
303
|
+
ARGV[0].casecmp(name.to_s) == 0
|
304
|
+
end unless ARGV.empty?
|
305
|
+
$VERBOSE = true
|
306
|
+
if dl
|
307
|
+
dl = Downloader.const_get(dl)
|
308
|
+
ARGV.shift
|
309
|
+
ARGV.each do |name|
|
310
|
+
dir = destdir
|
311
|
+
if prefix
|
312
|
+
name = name.sub(/\A\.\//, '')
|
313
|
+
if name.start_with?(destdir+"/")
|
314
|
+
name = name[(destdir.size+1)..-1]
|
315
|
+
if (dir = File.dirname(name)) == '.'
|
316
|
+
dir = destdir
|
317
|
+
else
|
318
|
+
dir = File.join(destdir, dir)
|
319
|
+
end
|
320
|
+
else
|
321
|
+
name = File.basename(name)
|
322
|
+
end
|
323
|
+
name = "#{prefix}/#{name}"
|
324
|
+
end
|
325
|
+
dl.download(name, dir, since, options)
|
326
|
+
end
|
327
|
+
else
|
328
|
+
abort "usage: #{$0} url name" unless ARGV.size == 2
|
329
|
+
Downloader.download(ARGV[0], ARGV[1], destdir, since, options)
|
330
|
+
end
|
331
|
+
end
|
data/bin/extlibs.rb
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# Used to download, extract and patch extension libraries (extlibs)
|
4
|
+
# for Ruby. See common.mk for Ruby's usage.
|
5
|
+
|
6
|
+
require 'digest'
|
7
|
+
require_relative 'downloader'
|
8
|
+
|
9
|
+
class Vars < Hash
|
10
|
+
def pattern
|
11
|
+
/\$\((#{Regexp.union(keys)})\)/
|
12
|
+
end
|
13
|
+
|
14
|
+
def expand(str)
|
15
|
+
if empty?
|
16
|
+
str
|
17
|
+
else
|
18
|
+
str.gsub(pattern) {self[$1]}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class ExtLibs
|
24
|
+
def cache_file(url, cache_dir)
|
25
|
+
Downloader.cache_file(url, nil, :cache_dir => cache_dir)
|
26
|
+
end
|
27
|
+
|
28
|
+
def do_download(url, cache_dir)
|
29
|
+
Downloader.download(url, nil, nil, nil, :cache_dir => cache_dir)
|
30
|
+
end
|
31
|
+
|
32
|
+
def do_checksum(cache, chksums)
|
33
|
+
chksums.each do |sum|
|
34
|
+
name, sum = sum.split(/:/)
|
35
|
+
if $VERBOSE
|
36
|
+
$stdout.print "checking #{name} of #{cache} ..."
|
37
|
+
$stdout.flush
|
38
|
+
end
|
39
|
+
hd = Digest(name.upcase).file(cache).hexdigest
|
40
|
+
if hd == sum
|
41
|
+
if $VERBOSE
|
42
|
+
$stdout.puts " OK"
|
43
|
+
$stdout.flush
|
44
|
+
end
|
45
|
+
else
|
46
|
+
if $VERBOSE
|
47
|
+
$stdout.puts " NG"
|
48
|
+
$stdout.flush
|
49
|
+
end
|
50
|
+
raise "checksum mismatch: #{cache}, #{name}:#{hd}, expected #{sum}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def do_extract(cache, dir)
|
56
|
+
if $VERBOSE
|
57
|
+
$stdout.puts "extracting #{cache} into #{dir}"
|
58
|
+
$stdout.flush
|
59
|
+
end
|
60
|
+
ext = File.extname(cache)
|
61
|
+
case ext
|
62
|
+
when '.gz', '.tgz'
|
63
|
+
f = IO.popen(["gzip", "-dc", cache])
|
64
|
+
cache = cache.chomp('.gz')
|
65
|
+
when '.bz2', '.tbz'
|
66
|
+
f = IO.popen(["bzip2", "-dc", cache])
|
67
|
+
cache = cache.chomp('.bz2')
|
68
|
+
when '.xz', '.txz'
|
69
|
+
f = IO.popen(["xz", "-dc", cache])
|
70
|
+
cache = cache.chomp('.xz')
|
71
|
+
else
|
72
|
+
inp = cache
|
73
|
+
end
|
74
|
+
inp ||= f.binmode
|
75
|
+
ext = File.extname(cache)
|
76
|
+
case ext
|
77
|
+
when '.tar', /\A\.t[gbx]z\z/
|
78
|
+
pid = Process.spawn("tar", "xpf", "-", in: inp, chdir: dir)
|
79
|
+
when '.zip'
|
80
|
+
pid = Process.spawn("unzip", inp, "-d", dir)
|
81
|
+
end
|
82
|
+
f.close if f
|
83
|
+
Process.wait(pid)
|
84
|
+
$?.success? or raise "failed to extract #{cache}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def do_patch(dest, patch, args)
|
88
|
+
if $VERBOSE
|
89
|
+
$stdout.puts "applying #{patch} under #{dest}"
|
90
|
+
$stdout.flush
|
91
|
+
end
|
92
|
+
Process.wait(Process.spawn("patch", "-d", dest, "-i", patch, *args))
|
93
|
+
$?.success? or raise "failed to patch #{patch}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def do_link(file, src, dest)
|
97
|
+
file = File.join(dest, file)
|
98
|
+
if (target = src).start_with?("/")
|
99
|
+
target = File.join([".."] * file.count("/"), src)
|
100
|
+
end
|
101
|
+
return unless File.exist?(File.expand_path(target, File.dirname(file)))
|
102
|
+
File.unlink(file) rescue nil
|
103
|
+
begin
|
104
|
+
File.symlink(target, file)
|
105
|
+
rescue
|
106
|
+
else
|
107
|
+
if $VERBOSE
|
108
|
+
$stdout.puts "linked #{target} to #{file}"
|
109
|
+
$stdout.flush
|
110
|
+
end
|
111
|
+
return
|
112
|
+
end
|
113
|
+
begin
|
114
|
+
src = src.sub(/\A\//, '')
|
115
|
+
File.copy_stream(src, file)
|
116
|
+
rescue
|
117
|
+
if $VERBOSE
|
118
|
+
$stdout.puts "failed to link #{src} to #{file}: #{$!.message}"
|
119
|
+
end
|
120
|
+
else
|
121
|
+
if $VERBOSE
|
122
|
+
$stdout.puts "copied #{src} to #{file}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def do_exec(command, dir, dest)
|
128
|
+
dir = dir ? File.join(dest, dir) : dest
|
129
|
+
if $VERBOSE
|
130
|
+
$stdout.puts "running #{command.dump} under #{dir}"
|
131
|
+
$stdout.flush
|
132
|
+
end
|
133
|
+
system(command, chdir: dir) or raise "failed #{command.dump}"
|
134
|
+
end
|
135
|
+
|
136
|
+
def do_command(mode, dest, url, cache_dir, chksums)
|
137
|
+
extracted = false
|
138
|
+
base = /.*(?=\.tar(?:\.\w+)?\z)/
|
139
|
+
|
140
|
+
case mode
|
141
|
+
when :download
|
142
|
+
cache = do_download(url, cache_dir)
|
143
|
+
do_checksum(cache, chksums)
|
144
|
+
when :extract
|
145
|
+
cache = cache_file(url, cache_dir)
|
146
|
+
target = File.join(dest, File.basename(cache)[base])
|
147
|
+
unless File.directory?(target)
|
148
|
+
do_checksum(cache, chksums)
|
149
|
+
extracted = do_extract(cache, dest)
|
150
|
+
end
|
151
|
+
when :all
|
152
|
+
cache = do_download(url, cache_dir)
|
153
|
+
target = File.join(dest, File.basename(cache)[base])
|
154
|
+
unless File.directory?(target)
|
155
|
+
do_checksum(cache, chksums)
|
156
|
+
extracted = do_extract(cache, dest)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
extracted
|
160
|
+
end
|
161
|
+
|
162
|
+
def run(argv)
|
163
|
+
cache_dir = nil
|
164
|
+
mode = :all
|
165
|
+
until argv.empty?
|
166
|
+
case argv[0]
|
167
|
+
when '--download'
|
168
|
+
mode = :download
|
169
|
+
when '--extract'
|
170
|
+
mode = :extract
|
171
|
+
when '--patch'
|
172
|
+
mode = :patch
|
173
|
+
when '--all'
|
174
|
+
mode = :all
|
175
|
+
when '--cache'
|
176
|
+
argv.shift
|
177
|
+
cache_dir = argv[0]
|
178
|
+
when /\A--cache=/
|
179
|
+
cache_dir = $'
|
180
|
+
when '--'
|
181
|
+
argv.shift
|
182
|
+
break
|
183
|
+
when /\A-/
|
184
|
+
warn "unknown option: #{argv[0]}"
|
185
|
+
return false
|
186
|
+
else
|
187
|
+
break
|
188
|
+
end
|
189
|
+
argv.shift
|
190
|
+
end
|
191
|
+
|
192
|
+
success = true
|
193
|
+
argv.each do |dir|
|
194
|
+
Dir.glob("#{dir}/**/extlibs") do |list|
|
195
|
+
if $VERBOSE
|
196
|
+
$stdout.puts "downloading for #{list}"
|
197
|
+
$stdout.flush
|
198
|
+
end
|
199
|
+
vars = Vars.new
|
200
|
+
extracted = false
|
201
|
+
dest = File.dirname(list)
|
202
|
+
url = chksums = nil
|
203
|
+
IO.foreach(list) do |line|
|
204
|
+
line.sub!(/\s*#.*/, '')
|
205
|
+
if /^(\w+)\s*=\s*(.*)/ =~ line
|
206
|
+
vars[$1] = vars.expand($2)
|
207
|
+
next
|
208
|
+
end
|
209
|
+
if chksums
|
210
|
+
chksums.concat(line.split)
|
211
|
+
elsif /^\t/ =~ line
|
212
|
+
if extracted and (mode == :all or mode == :patch)
|
213
|
+
patch, *args = line.split.map {|s| vars.expand(s)}
|
214
|
+
do_patch(dest, patch, args)
|
215
|
+
end
|
216
|
+
next
|
217
|
+
elsif /^!\s*(?:chdir:\s*([^|\s]+)\|\s*)?(.*)/ =~ line
|
218
|
+
if extracted and (mode == :all or mode == :patch)
|
219
|
+
command = vars.expand($2.strip)
|
220
|
+
chdir = $1 and chdir = vars.expand(chdir)
|
221
|
+
do_exec(command, chdir, dest)
|
222
|
+
end
|
223
|
+
next
|
224
|
+
elsif /->/ =~ line
|
225
|
+
if extracted and (mode == :all or mode == :patch)
|
226
|
+
link, file = $`.strip, $'.strip
|
227
|
+
do_link(vars.expand(link), vars.expand(file), dest)
|
228
|
+
end
|
229
|
+
next
|
230
|
+
else
|
231
|
+
url, *chksums = line.split(' ')
|
232
|
+
end
|
233
|
+
if chksums.last == '\\'
|
234
|
+
chksums.pop
|
235
|
+
next
|
236
|
+
end
|
237
|
+
unless url
|
238
|
+
chksums = nil
|
239
|
+
next
|
240
|
+
end
|
241
|
+
url = vars.expand(url)
|
242
|
+
begin
|
243
|
+
extracted = do_command(mode, dest, url, cache_dir, chksums)
|
244
|
+
rescue => e
|
245
|
+
warn e.inspect
|
246
|
+
success = false
|
247
|
+
end
|
248
|
+
url = chksums = nil
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
success
|
253
|
+
end
|
254
|
+
|
255
|
+
def self.run(argv)
|
256
|
+
self.new.run(argv)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
if $0 == __FILE__
|
261
|
+
exit ExtLibs.run(ARGV)
|
262
|
+
end
|