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 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