fiddle 1.0.0 → 1.0.1
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/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
|