nice-ffi 0.1 → 0.2
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/ChangeLog.txt +493 -0
- data/README.rdoc +23 -11
- data/docs/usage.rdoc +144 -37
- data/lib/nice-ffi.rb +10 -4
- data/lib/nice-ffi/autorelease.rb +112 -0
- data/lib/nice-ffi/{nicelibrary.rb → library.rb} +73 -45
- data/lib/nice-ffi/opaquestruct.rb +109 -0
- data/lib/nice-ffi/pathset.rb +404 -144
- data/lib/nice-ffi/{nicestruct.rb → struct.rb} +64 -19
- data/lib/nice-ffi/typedpointer.rb +20 -7
- metadata +11 -20
@@ -0,0 +1,109 @@
|
|
1
|
+
#--
|
2
|
+
#
|
3
|
+
# This file is one part of:
|
4
|
+
#
|
5
|
+
# Nice-FFI - Convenience layer atop Ruby-FFI
|
6
|
+
#
|
7
|
+
# Copyright (c) 2009 John Croisant
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# "Software"), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
23
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
24
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
25
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
26
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
#
|
28
|
+
#++
|
29
|
+
|
30
|
+
|
31
|
+
# OpaqueStruct represents a Struct with an unknown layout.
|
32
|
+
# This is meant to be used when the C library designer has
|
33
|
+
# intentionally hidden the layout (e.g. to prevent user access).
|
34
|
+
#
|
35
|
+
# Because the size of an OpaqueStruct is unknown, you should
|
36
|
+
# only use methods provided by the C library to allocate, modify,
|
37
|
+
# or free the struct's memory.
|
38
|
+
#
|
39
|
+
# OpaqueStruct supports the same memory autorelease system as
|
40
|
+
# NiceStruct. Define MyClass.release( pointer ) to call the library
|
41
|
+
# function to free the struct, and pass an FFI::Pointer to #new. You
|
42
|
+
# can disable autorelease for an individual instance by providing
|
43
|
+
# {:autorelease => false} as an option to #new
|
44
|
+
#
|
45
|
+
class NiceFFI::OpaqueStruct
|
46
|
+
include NiceFFI::AutoRelease
|
47
|
+
|
48
|
+
|
49
|
+
# Returns a NiceFFI::TypedPointer instance for this class.
|
50
|
+
def self.typed_pointer
|
51
|
+
@typed_pointer or (@typed_pointer = NiceFFI::TypedPointer.new(self))
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# Create a new instance of the class, wrapping (not copying!) a
|
56
|
+
# FFI::Pointer. You can pass another instance of this class to
|
57
|
+
# create a new instance wrapping the same pointer.
|
58
|
+
#
|
59
|
+
# If val is an instance of FFI::Pointer and you have defined
|
60
|
+
# MyClass.release, the pointer will be passed to MyClass.release
|
61
|
+
# when the memory is no longer being used. Use MyClass.release to
|
62
|
+
# free the memory for the struct, as appropriate for your class. To
|
63
|
+
# disable autorelease for this instance, set {:autorelease => false}
|
64
|
+
# in +options+.
|
65
|
+
#
|
66
|
+
# (Note: FFI::MemoryPointer and FFI::Buffer have built-in memory
|
67
|
+
# management, so MyClass.release is never called for them.)
|
68
|
+
#
|
69
|
+
def initialize( val, options={} )
|
70
|
+
options = {:autorelease => true}.merge!( options )
|
71
|
+
|
72
|
+
case val
|
73
|
+
|
74
|
+
when self.class
|
75
|
+
initialize( val.pointer, options )
|
76
|
+
|
77
|
+
when FFI::AutoPointer
|
78
|
+
@pointer = val
|
79
|
+
|
80
|
+
when FFI::Pointer
|
81
|
+
if val.is_a? FFI::MemoryPointer or val.is_a? FFI::Buffer
|
82
|
+
raise TypeError, "unsupported pointer type #{val.class.name}"
|
83
|
+
elsif val.null?
|
84
|
+
@pointer = val
|
85
|
+
else
|
86
|
+
@pointer = _make_autopointer( val, options[:autorelease] )
|
87
|
+
end
|
88
|
+
|
89
|
+
else
|
90
|
+
raise TypeError, "cannot create new #{self.class} from #{val.inspect}"
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
attr_reader :pointer
|
97
|
+
|
98
|
+
def to_ptr
|
99
|
+
@pointer
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def to_s
|
104
|
+
"#<%s:%#.x>"%[self.class.name, self.object_id]
|
105
|
+
end
|
106
|
+
|
107
|
+
alias :inspect :to_s
|
108
|
+
|
109
|
+
end
|
data/lib/nice-ffi/pathset.rb
CHANGED
@@ -28,45 +28,102 @@
|
|
28
28
|
#++
|
29
29
|
|
30
30
|
|
31
|
-
# PathSet is
|
32
|
-
#
|
33
|
-
#
|
31
|
+
# PathSet is a collection of directory paths and file name templates,
|
32
|
+
# used to help NiceFFI find library files. It allows per-operating
|
33
|
+
# system paths and file name templates, using regular expressions to
|
34
|
+
# match the OS name.
|
34
35
|
#
|
35
|
-
#
|
36
|
-
# for the operating system(s) that the path templates are for.
|
36
|
+
# Each PathSet holds two hashes, @paths and @files.
|
37
37
|
#
|
38
|
-
# *
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# So "/usr/lib/lib[NAME].so" becomes e.g. "/usr/lib/libSDL_ttf.so".
|
38
|
+
# * The keys for both @paths and @files are regexps that match
|
39
|
+
# FFI::Platform::OS for the operating system(s) that the paths or
|
40
|
+
# file templates apply to.
|
42
41
|
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
42
|
+
# * The values of @paths are Arrays of one or more strings describing
|
43
|
+
# a directory for where a library might be found on this OS. So for
|
44
|
+
# example, one pair in @paths might be { /linux|bsd/ =>
|
45
|
+
# ["/usr/local/lib/", "/usr/lib/"] }, which means: "For operating
|
46
|
+
# systems that match the regular expression /linux|bsd/ (e.g.
|
47
|
+
# 'linux', 'freebsd', and 'openbsd'), look for libraries first in
|
48
|
+
# the directory '/usr/local/lib/', then in '/usr/lib/'."
|
49
|
+
#
|
50
|
+
# * The value of @files are Arrays of one or more strings describing
|
51
|
+
# the possible formats of library names for that operating system.
|
52
|
+
# These are templates -- they should include string "[NAME]",
|
53
|
+
# which will be replaced with the library name. For example,
|
54
|
+
# "lib[NAME].so" would become "libSDL_ttf.so" when searching for the
|
55
|
+
# "SDL_ttf" library.
|
56
|
+
#
|
57
|
+
# There are several methods to modify @paths and/or @files. See
|
58
|
+
# #append, #prepend, #replace, #remove, and #delete.
|
59
|
+
#
|
60
|
+
# Once @paths and @files are set up, use #find to look for a file with
|
61
|
+
# a matching name.
|
62
|
+
#
|
63
|
+
# NiceFFI::PathSet::DEFAULT is a pre-made PathSet with paths and file
|
64
|
+
# name templates for Linux/BSD, Mac (Darwin), and Windows. It is the
|
65
|
+
# default PathSet used by NiceFFI::Library.load_library, and you can
|
66
|
+
# also use it as a base for custom PathSets.
|
46
67
|
#
|
47
68
|
class NiceFFI::PathSet
|
48
69
|
|
49
70
|
|
50
|
-
def initialize(
|
51
|
-
@
|
71
|
+
def initialize( paths={}, files={} )
|
72
|
+
@paths = {}
|
73
|
+
@files = {}
|
74
|
+
append!( :paths, paths )
|
75
|
+
append!( :files, files )
|
52
76
|
end
|
53
77
|
|
54
|
-
attr_reader :
|
78
|
+
attr_reader :paths, :files
|
55
79
|
|
56
80
|
def dup
|
57
|
-
self.class.new( @
|
81
|
+
self.class.new( @paths.dup, @files.dup )
|
58
82
|
end
|
59
83
|
|
60
84
|
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
85
|
+
# call-seq:
|
86
|
+
# append( *entries )
|
87
|
+
# append( option, *entries )
|
88
|
+
#
|
89
|
+
# Create a copy of this PathSet and append the new paths and/or
|
90
|
+
# files. If the copy already has entries for a given regexp, the
|
91
|
+
# new entries will be added after the current entries.
|
92
|
+
#
|
93
|
+
# option:: You can optionally give either :paths or :files as the
|
94
|
+
# first argument to this method. If :paths, only @paths
|
95
|
+
# will be modified, @files will never be modified. If
|
96
|
+
# :files, only @files will be modified, @paths will never
|
97
|
+
# be modified.
|
64
98
|
#
|
65
|
-
#
|
66
|
-
#
|
99
|
+
# entries:: One or more PathSets, Hashes, Arrays, or Strings,
|
100
|
+
# or any assortment of these types.
|
67
101
|
#
|
102
|
+
# * If given a PathSet, its @paths and @files are appended to the
|
103
|
+
# copy's @paths and @files (respectively). If option is :paths,
|
104
|
+
# only @paths is modified. If option is :files, only @files is
|
105
|
+
# modified.
|
68
106
|
#
|
69
|
-
#
|
107
|
+
# * If given a Hash, it is appended to the copy's @paths, but
|
108
|
+
# @files is not affected. If option is :files, @files is modified
|
109
|
+
# instead of @paths.
|
110
|
+
#
|
111
|
+
# * If given an Array (which should contain only Strings), the array
|
112
|
+
# contents are appended to the copy's @paths. If option is
|
113
|
+
# :files, @files is modified instead of @paths.
|
114
|
+
#
|
115
|
+
# * If given a String, the string is appended to the copy's
|
116
|
+
# @paths. If option is :files, @files is modified instead of
|
117
|
+
# @paths.
|
118
|
+
#
|
119
|
+
# * If given multiple objects, they are handled in order according to
|
120
|
+
# the above rules.
|
121
|
+
#
|
122
|
+
# See also #append! for a version of this method which modifies self
|
123
|
+
# instead of making a copy.
|
124
|
+
#
|
125
|
+
#--
|
126
|
+
# Example (out of date):
|
70
127
|
#
|
71
128
|
# ps = PathSet.new( /a/ => ["liba"],
|
72
129
|
# /b/ => ["libb"] )
|
@@ -74,38 +131,70 @@ class NiceFFI::PathSet
|
|
74
131
|
# ps.append!( /a/ => ["newliba"],
|
75
132
|
# /c/ => ["libc"] )
|
76
133
|
#
|
77
|
-
# ps.
|
134
|
+
# ps.paths
|
78
135
|
# # => { /a/ => ["liba",
|
79
136
|
# # "newliba"], # added in back
|
80
137
|
# # /b/ => ["libb"], # not affected
|
81
138
|
# # /c/ => ["libc"] } # added
|
82
|
-
|
83
|
-
def append
|
84
|
-
|
85
|
-
_modify( rules ) { |a,b| a + b }
|
86
|
-
end
|
87
|
-
self
|
139
|
+
#++
|
140
|
+
def append( *entries )
|
141
|
+
self.dup.append!( *entries )
|
88
142
|
end
|
89
143
|
|
90
|
-
#
|
91
|
-
|
92
|
-
|
144
|
+
# call-seq:
|
145
|
+
# append!( *entries )
|
146
|
+
# append!( option, *entries )
|
147
|
+
#
|
148
|
+
# Like #append, but modifies self instead of making a copy.
|
149
|
+
def append!( *entries )
|
150
|
+
_modify( *entries ) { |a,b| a + b }
|
93
151
|
end
|
94
152
|
|
95
153
|
alias :+ :append
|
96
|
-
alias :<< :append
|
97
154
|
|
98
155
|
|
99
|
-
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
156
|
+
# call-seq:
|
157
|
+
# prepend( *entries )
|
158
|
+
# prepend( option, *entries )
|
159
|
+
#
|
160
|
+
# Creates a copy of this PathSet and prepends the new paths and/or
|
161
|
+
# files. If the copy already has entries for a given regexp, the
|
162
|
+
# new entries will be added before the current entries.
|
163
|
+
#
|
164
|
+
# option:: You can optionally give either :paths or :files as the
|
165
|
+
# first argument to this method. If :paths, only @paths
|
166
|
+
# will be modified, @files will never be modified. If
|
167
|
+
# :files, only @files will be modified, @paths will never
|
168
|
+
# be modified.
|
103
169
|
#
|
104
|
-
#
|
105
|
-
#
|
170
|
+
# entries:: One or more PathSets, Hashes, Arrays, or Strings,
|
171
|
+
# or any assortment of these types.
|
106
172
|
#
|
173
|
+
# * If given a PathSet, its @paths and @files are prepended to this
|
174
|
+
# PathSet's @paths and @files (respectively). If option is :paths,
|
175
|
+
# only @paths is modified. If option is :files, only @files is
|
176
|
+
# modified.
|
177
|
+
#
|
178
|
+
# * If given a Hash, it is prepended to the copy's @paths, but
|
179
|
+
# @files is not affected. If option is :files, @files is modified
|
180
|
+
# instead of @paths.
|
107
181
|
#
|
108
|
-
#
|
182
|
+
# * If given an Array (which should contain only Strings), the array
|
183
|
+
# contents are prepended to the copy's @paths. If option is
|
184
|
+
# :files, @files is modified instead of @paths.
|
185
|
+
#
|
186
|
+
# * If given a String, the string is prepended to the copy's
|
187
|
+
# @paths. If option is :files, @files is modified instead of
|
188
|
+
# @paths.
|
189
|
+
#
|
190
|
+
# * If given multiple objects, they are handled in order according to
|
191
|
+
# the above rules.
|
192
|
+
#
|
193
|
+
# See also #prepend! for a version of this method which modifies self
|
194
|
+
# instead of making a copy.
|
195
|
+
#
|
196
|
+
#--
|
197
|
+
# Example (out of date):
|
109
198
|
#
|
110
199
|
# ps = PathSet.new( /a/ => ["liba"],
|
111
200
|
# /b/ => ["libb"] )
|
@@ -113,38 +202,74 @@ class NiceFFI::PathSet
|
|
113
202
|
# ps.prepend!( /a/ => ["newliba"],
|
114
203
|
# /c/ => ["libc"] )
|
115
204
|
#
|
116
|
-
# ps.
|
205
|
+
# ps.paths
|
117
206
|
# # => { /a/ => ["newliba", # added in front
|
118
207
|
# # "liba"],
|
119
208
|
# # /b/ => ["libb"], # not affected
|
120
209
|
# # /c/ => ["libc"] } # added
|
121
|
-
|
122
|
-
def prepend
|
123
|
-
|
124
|
-
_modify( rules ) { |a,b| b + a }
|
125
|
-
end
|
126
|
-
self
|
210
|
+
#++
|
211
|
+
def prepend( *entries )
|
212
|
+
self.dup.prepend!( *entries )
|
127
213
|
end
|
128
214
|
|
129
|
-
#
|
130
|
-
|
131
|
-
|
215
|
+
# call-seq:
|
216
|
+
# prepend!( *entries )
|
217
|
+
# prepend!( option, *entries )
|
218
|
+
#
|
219
|
+
# Like #prepend, but modifies self instead of making a copy.
|
220
|
+
def prepend!( *entries )
|
221
|
+
_modify( *entries ) { |a,b| b + a }
|
132
222
|
end
|
133
223
|
|
134
|
-
alias :>> :prepend
|
135
224
|
|
136
225
|
|
137
|
-
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
226
|
+
# call-seq:
|
227
|
+
# replace( *entries )
|
228
|
+
# replace( option, *entries )
|
229
|
+
#
|
230
|
+
# Creates a copy of this PathSet and overrides existing entries with
|
231
|
+
# the new entries. If the copy already has entries for a regexp
|
232
|
+
# in the new entries, the old entries will be discarded and the new
|
233
|
+
# entries used instead.
|
234
|
+
#
|
235
|
+
# option:: You can optionally give either :paths or :files as the
|
236
|
+
# first argument to this method. If :paths, only @paths
|
237
|
+
# will be modified, @files will never be modified. If
|
238
|
+
# :files, only @files will be modified, @paths will never
|
239
|
+
# be modified.
|
240
|
+
#
|
241
|
+
# entries:: One or more PathSets, Hashes, Arrays, or Strings,
|
242
|
+
# or any assortment of these types.
|
243
|
+
#
|
244
|
+
# * If given a PathSet, the copy's @paths and @files with the
|
245
|
+
# other PathSet's @paths and @files (respectively). Old entries in
|
246
|
+
# the copy are kept if their regexp doesn't appear in the given
|
247
|
+
# PathSet. If option is :paths, only @paths is modified. If option
|
248
|
+
# is :files, only @files is modified.
|
249
|
+
#
|
250
|
+
# * If given a Hash, entries in the copy's @paths are replaced
|
251
|
+
# with the new entries, but @files is not affected. Old entries in
|
252
|
+
# the copy are kept if their regexp doesn't appear in the given
|
253
|
+
# PathSet. If option is :files, @files is modified instead of
|
254
|
+
# @paths.
|
255
|
+
#
|
256
|
+
# * If given an Array (which should contain only Strings), entries
|
257
|
+
# for every regexp in the copy's @paths are replaced with the
|
258
|
+
# array contents. If option is :files, @files is modified instead
|
259
|
+
# of @paths.
|
260
|
+
#
|
261
|
+
# * If given a String, all entries for every regexp in the copy's
|
262
|
+
# @paths are replaced with the string. If option is :files, @files
|
263
|
+
# is modified instead of @paths.
|
142
264
|
#
|
143
|
-
#
|
144
|
-
#
|
265
|
+
# * If given multiple objects, they are handled in order according to
|
266
|
+
# the above rules.
|
145
267
|
#
|
268
|
+
# See also #replace! for a version of this method which modifies self
|
269
|
+
# instead of making a copy.
|
146
270
|
#
|
147
|
-
|
271
|
+
#--
|
272
|
+
# Example (out of date):
|
148
273
|
#
|
149
274
|
# ps = PathSet.new( /a/ => ["liba"],
|
150
275
|
# /b/ => ["libb"] )
|
@@ -152,36 +277,70 @@ class NiceFFI::PathSet
|
|
152
277
|
# ps.replace!( /a/ => ["newliba"],
|
153
278
|
# /c/ => ["libc"] )
|
154
279
|
#
|
155
|
-
# ps.
|
280
|
+
# ps.paths
|
156
281
|
# # => { /a/ => ["newliba"], # replaced
|
157
282
|
# # /b/ => ["libb"], # not affected
|
158
283
|
# # /c/ => ["libc"] } # added
|
159
|
-
|
160
|
-
def replace
|
161
|
-
|
162
|
-
_modify( rules ) { |a,b| b }
|
163
|
-
end
|
164
|
-
self
|
284
|
+
#++
|
285
|
+
def replace( *entries )
|
286
|
+
self.dup.replace!( *entries )
|
165
287
|
end
|
166
288
|
|
167
|
-
#
|
168
|
-
|
169
|
-
|
289
|
+
# call-seq:
|
290
|
+
# replace!( *entries )
|
291
|
+
# replace!( option, *entries )
|
292
|
+
#
|
293
|
+
# Like #replace, but modifies self instead of making a copy.
|
294
|
+
def replace!( *entries )
|
295
|
+
_modify( *entries ) { |a,b| b }
|
170
296
|
end
|
171
297
|
|
172
298
|
|
173
299
|
|
174
|
-
#
|
175
|
-
#
|
176
|
-
#
|
300
|
+
# call-seq:
|
301
|
+
# remove( *entries )
|
302
|
+
# remove( option, *entries )
|
303
|
+
#
|
304
|
+
# Creates a copy of this PathSet and removes the given entries from
|
305
|
+
# the copy, if it has them. This only removes the entries that are
|
306
|
+
# given, other entries for the same regexp are kept. Regexps with no
|
307
|
+
# entries left afterwards are removed from the PathSet.
|
308
|
+
#
|
309
|
+
# option:: You can optionally give either :paths or :files as the
|
310
|
+
# first argument to this method. If :paths, only @paths
|
311
|
+
# will be modified, @files will never be modified. If
|
312
|
+
# :files, only @files will be modified, @paths will never
|
313
|
+
# be modified.
|
177
314
|
#
|
178
|
-
#
|
179
|
-
#
|
315
|
+
# entries:: One or more PathSets, Hashes, Arrays, or Strings,
|
316
|
+
# or any assortment of these types.
|
180
317
|
#
|
181
|
-
#
|
318
|
+
# * If given a PathSet, entries from its @paths and @files are
|
319
|
+
# removed from the copy's @paths and @files (respectively). If
|
320
|
+
# option is :paths, only @paths is modified. If option is :files,
|
321
|
+
# only @files is modified.
|
182
322
|
#
|
323
|
+
# * If given a Hash, the given entries are removed from this
|
324
|
+
# PathSet's @paths, but @files is not affected. If option is
|
325
|
+
# :files, @files is modified instead of @paths.
|
183
326
|
#
|
184
|
-
#
|
327
|
+
# * If given an Array (which should contain only Strings), the array
|
328
|
+
# contents are removed from the entries for every regexp in this
|
329
|
+
# PathSet's @paths. If option is :files, @files is modified
|
330
|
+
# instead of @paths.
|
331
|
+
#
|
332
|
+
# * If given a String, the string is removed from the entries for
|
333
|
+
# every regexp in the copy's @paths. If option is :files,
|
334
|
+
# @files is modified instead of @paths.
|
335
|
+
#
|
336
|
+
# * If given multiple objects, they are handled in order according to
|
337
|
+
# the above rules.
|
338
|
+
#
|
339
|
+
# See also #remove! for a version of this method which modifies self
|
340
|
+
# instead of making a copy.
|
341
|
+
#
|
342
|
+
#--
|
343
|
+
# Example (out of date):
|
185
344
|
#
|
186
345
|
# ps = PathSet.new( /a/ => ["liba", "badliba"],
|
187
346
|
# /b/ => ["libb"] )
|
@@ -190,55 +349,88 @@ class NiceFFI::PathSet
|
|
190
349
|
# /b/ => ["libb"] )
|
191
350
|
# /c/ => ["libc"] )
|
192
351
|
#
|
193
|
-
# ps.
|
352
|
+
# ps.paths
|
194
353
|
# # => { /a/ => ["liba"] } # removed only "badliba".
|
195
|
-
# # # /b/
|
196
|
-
# # # /c/ not affected because it had no old
|
197
|
-
|
198
|
-
def remove
|
199
|
-
|
200
|
-
_modify( rules ) { |a,b| a - b }
|
201
|
-
end
|
202
|
-
self
|
354
|
+
# # # /b/ paths were all removed.
|
355
|
+
# # # /c/ not affected because it had no old paths anyway.
|
356
|
+
#++
|
357
|
+
def remove( *entries )
|
358
|
+
self.dup.remove!( *entries )
|
203
359
|
end
|
204
360
|
|
205
|
-
#
|
206
|
-
|
207
|
-
|
361
|
+
# call-seq:
|
362
|
+
# remove!( *entries )
|
363
|
+
# remove!( option, *entries )
|
364
|
+
#
|
365
|
+
# Like #remove, but modifies self instead of making a copy.
|
366
|
+
def remove!( *entries )
|
367
|
+
_modify( *entries ) { |a,b| a - b }
|
208
368
|
end
|
209
369
|
|
210
370
|
alias :- :remove
|
211
371
|
|
212
372
|
|
213
373
|
|
214
|
-
#
|
215
|
-
#
|
374
|
+
# call-seq:
|
375
|
+
# delete( *regexps )
|
376
|
+
# delete( option, *regexps )
|
377
|
+
#
|
378
|
+
# Creates a copy of this PathSet and delete all entries from the
|
379
|
+
# copy for the given regexp(s) from @paths and/or @files. Has no
|
380
|
+
# effect on entries for regexps that are not given.
|
381
|
+
#
|
382
|
+
# option:: You can optionally give either :paths or :files as the
|
383
|
+
# first argument to this method. If :paths, only @paths
|
384
|
+
# will be modified, @files will never be modified. If
|
385
|
+
# :files, only @files will be modified, @paths will never
|
386
|
+
# be modified.
|
387
|
+
#
|
388
|
+
# regexps:: One or more Regexps to remove entries for.
|
216
389
|
#
|
390
|
+
# See also #delete! for a version of this method which modifies self
|
391
|
+
# instead of making a copy.
|
217
392
|
#
|
218
|
-
|
393
|
+
#--
|
394
|
+
# Example (out of date):
|
219
395
|
#
|
220
396
|
# ps = PathSet.new( /a/ => ["liba"],
|
221
397
|
# /b/ => ["libb"] )
|
222
398
|
#
|
223
399
|
# ps.delete!( /b/, /c/ )
|
224
400
|
#
|
225
|
-
# ps.
|
401
|
+
# ps.paths
|
226
402
|
# # => { /a/ => ["liba"] } # not affected
|
227
|
-
# # # /b/ and all
|
228
|
-
# # # /c/ not affected because it had no
|
403
|
+
# # # /b/ and all paths removed.
|
404
|
+
# # # /c/ not affected because it had no paths anyway.
|
405
|
+
#++
|
406
|
+
def delete( *regexps )
|
407
|
+
self.dup.delete!( *regexps )
|
408
|
+
end
|
409
|
+
|
410
|
+
# call-seq:
|
411
|
+
# delete!( *regexps )
|
412
|
+
# delete!( option, *regexps )
|
229
413
|
#
|
230
|
-
|
231
|
-
|
414
|
+
# Like #delete, but modifies self instead of making a copy.
|
415
|
+
def delete!( *regexps )
|
416
|
+
case regexps[0]
|
417
|
+
when :paths
|
418
|
+
@paths.delete_if { |regexp, paths| regexps.include? regexp }
|
419
|
+
when :files
|
420
|
+
@files.delete_if { |regexp, files| regexps.include? regexp }
|
421
|
+
when Symbol
|
422
|
+
raise( "Invalid symbol option '#{first.inspect}'. " +
|
423
|
+
"Expected :paths or :files." )
|
424
|
+
else
|
425
|
+
@paths.delete_if { |regexp, paths| regexps.include? regexp }
|
426
|
+
@files.delete_if { |regexp, files| regexps.include? regexp }
|
427
|
+
end
|
232
428
|
self
|
233
429
|
end
|
234
430
|
|
235
|
-
# Like #delete!, but returns a copy instead of modifying the original.
|
236
|
-
def delete( *regexs )
|
237
|
-
self.dup.delete!( *regexs )
|
238
|
-
end
|
239
431
|
|
240
432
|
|
241
|
-
# Try to find a file based on the
|
433
|
+
# Try to find a file based on the paths in this PathSet.
|
242
434
|
#
|
243
435
|
# *names:: Strings to try substituting for [NAME] in the paths.
|
244
436
|
#
|
@@ -248,86 +440,154 @@ class NiceFFI::PathSet
|
|
248
440
|
# Raises LoadError if the current operating system did not match
|
249
441
|
# any of the regular expressions in the PathSet.
|
250
442
|
#
|
251
|
-
|
443
|
+
#--
|
444
|
+
# Examples (out of date):
|
252
445
|
#
|
253
|
-
# ps = PathSet.new( /linux/
|
254
|
-
# /
|
446
|
+
# ps = PathSet.new( /linux/ => ["/usr/lib/lib[NAME].so"],
|
447
|
+
# /windows/ => ["C:\\windows\\system32\\[NAME].dll"] )
|
255
448
|
#
|
256
449
|
# ps.find( "SDL" )
|
257
450
|
# ps.find( "foo", "foo_alt_name" )
|
258
|
-
|
451
|
+
#++
|
259
452
|
def find( *names )
|
260
|
-
|
261
453
|
os = FFI::Platform::OS
|
262
454
|
|
263
|
-
#
|
264
|
-
|
265
|
-
|
266
|
-
# Remember whether any of the search paths included our OS.
|
267
|
-
os_supported = false
|
455
|
+
# Fetch the paths and files for the matching OSes.
|
456
|
+
paths = @paths.collect{ |regexp,ps| regexp =~ os ? ps : [] }.flatten
|
457
|
+
files = @files.collect{ |regexp,fs| regexp =~ os ? fs : [] }.flatten
|
268
458
|
|
269
|
-
#
|
270
|
-
|
271
|
-
|
272
|
-
# Drat, they are using an unsupported OS.
|
273
|
-
if os_matches.empty?
|
459
|
+
# Drat, they are using an OS with no matches.
|
460
|
+
if paths.empty? and files.empty?
|
274
461
|
raise( LoadError, "Your OS (#{os}) is not supported yet.\n" +
|
275
462
|
"Please report this and help us support more platforms." )
|
276
463
|
end
|
277
464
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
File.expand_path( path.gsub("[NAME]", name) )
|
286
|
-
}
|
287
|
-
}.flatten!
|
288
|
-
|
289
|
-
# Delete all the paths that don't exist.
|
290
|
-
paths.delete_if { |path| not File.exist?(path) }
|
291
|
-
|
292
|
-
# Add what's left.
|
293
|
-
found += paths
|
465
|
+
results = paths.collect do |path|
|
466
|
+
files.collect do |file|
|
467
|
+
names.collect do |name|
|
468
|
+
# Concat path and file, fill in for [NAME], and expand.
|
469
|
+
File.expand_path( (path+file).gsub("[NAME]", name) )
|
470
|
+
end
|
471
|
+
end
|
294
472
|
end
|
295
473
|
|
296
|
-
return
|
474
|
+
return results.flatten.select{ |r| File.exist? r }
|
297
475
|
end
|
298
476
|
|
299
477
|
|
300
478
|
private
|
301
479
|
|
302
480
|
|
303
|
-
def _modify(
|
481
|
+
def _modify( *entries, &block )
|
482
|
+
# could be :paths or :files, or perhaps an entry
|
483
|
+
part = entries[0]
|
484
|
+
|
485
|
+
case part
|
486
|
+
when :paths, :files, :default
|
487
|
+
entries = entries[1..-1]
|
488
|
+
when Symbol # other symbols are invalid
|
489
|
+
raise( "Invalid symbol option '#{first.inspect}'. " +
|
490
|
+
"Expected :paths or :files." )
|
491
|
+
else
|
492
|
+
part = :default
|
493
|
+
end
|
494
|
+
|
495
|
+
entries.each do |entry|
|
496
|
+
if entry.kind_of? self.class
|
497
|
+
if part == :default
|
498
|
+
_modify_set( :paths, entry.paths, &block )
|
499
|
+
_modify_set( :files, entry.files, &block )
|
500
|
+
else
|
501
|
+
_modify_set( part, entry.send(part), &block )
|
502
|
+
end
|
503
|
+
else
|
504
|
+
if part == :default
|
505
|
+
_modify_set( :paths, entry, &block )
|
506
|
+
else
|
507
|
+
_modify_set( part, entry, &block )
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
return self
|
513
|
+
end
|
514
|
+
|
515
|
+
|
516
|
+
def _modify_set( ours, other, &block ) # :nodoc:
|
304
517
|
raise "No block given!" unless block_given?
|
305
518
|
|
306
|
-
case
|
307
|
-
|
308
|
-
|
519
|
+
ours = case ours
|
520
|
+
when :paths; @paths
|
521
|
+
when :files; @files
|
522
|
+
else
|
523
|
+
raise( "Invalid symbol option '#{ours.inspect}'. " +
|
524
|
+
"Expected :paths or :files." )
|
525
|
+
end
|
526
|
+
|
527
|
+
case other
|
309
528
|
when Hash
|
310
|
-
|
311
|
-
|
529
|
+
# Apply each of the regexps in `other` to the same regexp in `ours`
|
530
|
+
other.each do |regexp, paths|
|
531
|
+
_apply_modifier( ours, regexp, (ours[regexp] or []), paths, &block )
|
312
532
|
end
|
313
533
|
when Array
|
314
|
-
|
315
|
-
|
534
|
+
# Apply `other` to each of the regexps in `ours`
|
535
|
+
ours.each { |regexp, paths|
|
536
|
+
_apply_modifier( ours, regexp, paths, other, &block )
|
537
|
+
}
|
538
|
+
when String
|
539
|
+
# Apply an Array holding `other` to each of the regexps in `ours`
|
540
|
+
ours.each { |regexp, paths|
|
541
|
+
_apply_modifier( ours, regexp, paths, [other], &block )
|
316
542
|
}
|
317
543
|
end
|
318
544
|
end
|
319
545
|
|
320
546
|
|
321
|
-
def _apply_modifier(
|
547
|
+
def _apply_modifier( ours, regexp, a, b, &block ) # :nodoc:
|
322
548
|
raise "No block given!" unless block_given?
|
323
549
|
|
324
550
|
result = yield( a, b )
|
325
551
|
|
326
552
|
if result == []
|
327
|
-
|
553
|
+
ours.delete( regexp )
|
328
554
|
else
|
329
|
-
|
555
|
+
ours[regexp] = result
|
330
556
|
end
|
331
557
|
end
|
332
558
|
|
333
559
|
end
|
560
|
+
|
561
|
+
|
562
|
+
|
563
|
+
#--
|
564
|
+
# NOTE: If you update these defaults, update doc/usage.rdoc too.
|
565
|
+
#++
|
566
|
+
|
567
|
+
paths = {
|
568
|
+
/linux|bsd/ => [ "/usr/local/lib/",
|
569
|
+
"/usr/lib/" ],
|
570
|
+
|
571
|
+
/darwin/ => [ "/usr/local/lib/",
|
572
|
+
"/sw/lib/",
|
573
|
+
"/opt/local/lib/",
|
574
|
+
"~/Library/Frameworks/",
|
575
|
+
"/Library/Frameworks/" ],
|
576
|
+
|
577
|
+
/windows/ => [ "C:\\windows\\system32\\",
|
578
|
+
"C:\\windows\\system\\" ]
|
579
|
+
}
|
580
|
+
|
581
|
+
files = {
|
582
|
+
/linux|bsd/ => [ "lib[NAME].so" ],
|
583
|
+
|
584
|
+
/darwin/ => [ "lib[NAME].dylib",
|
585
|
+
"[NAME].framework/[NAME]" ],
|
586
|
+
|
587
|
+
/windows/ => [ "[NAME].dll" ]
|
588
|
+
}
|
589
|
+
|
590
|
+
# The default paths to look for libraries. See PathSet
|
591
|
+
# and NiceFFI::Library.load_library.
|
592
|
+
#
|
593
|
+
NiceFFI::PathSet::DEFAULT = NiceFFI::PathSet.new( paths, files )
|