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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 538e522b63f6cd2ec6ee72c138757966cc6e0aab44b381bb01bc464a740aa3a7
4
- data.tar.gz: 3fc2e992e471105c4cadd9ee58234c047e7f8899d38ef8476b05a1640ef40284
3
+ metadata.gz: 6273f6abe03da289f77f31e82b7d43fcb99e7144f096319306e91e6d0bc33659
4
+ data.tar.gz: de279d21f060c1726c701adc9dba5518bb85b179d3ac4dfd716b995948147163
5
5
  SHA512:
6
- metadata.gz: a1b2011849c78991b1129999a0469318a60414d3bd5db196cc82a21c86443a6f68cdbce1a3baef1043539e8395641c2ceac121fdf3e712bbc51ad9c2d4dcd12b
7
- data.tar.gz: 4a8fc35a372b73f62496760f38f6f0291fe6cbd649c634299b289bf006e46ab585024c7721ef02136f9d2ad45aa1136b6072ab6d7adcbd1bb2081d606cc870fb
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
- Rake::TestTask.new(:test) do |t|
5
- t.libs << "test" << "test/lib"
6
- t.libs << "lib"
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'
@@ -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
@@ -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