ffi 0.6.0 → 1.0.0
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/History.txt +109 -0
- data/LICENSE +10 -21
- data/README.rdoc +1 -0
- data/Rakefile +3 -2
- data/ext/ffi_c/AbstractMemory.c +56 -38
- data/ext/ffi_c/AbstractMemory.h +15 -22
- data/ext/ffi_c/Buffer.c +61 -22
- data/ext/ffi_c/Call.c +55 -543
- data/ext/ffi_c/Call.h +1 -1
- data/ext/ffi_c/DataConverter.c +62 -0
- data/ext/ffi_c/DynamicLibrary.c +21 -1
- data/ext/ffi_c/Function.c +255 -32
- data/ext/ffi_c/MappedType.c +146 -0
- data/ext/ffi_c/MappedType.h +57 -0
- data/ext/ffi_c/MemoryPointer.c +12 -33
- data/ext/ffi_c/Platform.c +2 -0
- data/ext/ffi_c/Pointer.c +66 -28
- data/ext/ffi_c/Struct.c +106 -318
- data/ext/ffi_c/Struct.h +6 -0
- data/ext/ffi_c/StructByReference.c +150 -0
- data/ext/ffi_c/StructByReference.h +50 -0
- data/ext/ffi_c/StructLayout.c +25 -14
- data/ext/ffi_c/Type.c +39 -68
- data/ext/ffi_c/Type.h +12 -22
- data/ext/ffi_c/Types.c +21 -6
- data/ext/ffi_c/Types.h +7 -7
- data/ext/ffi_c/Variadic.c +21 -17
- data/ext/ffi_c/extconf.rb +4 -0
- data/ext/ffi_c/ffi.c +8 -2
- data/ext/ffi_c/libffi/configure +1 -1
- data/ext/ffi_c/libffi.gnu.mk +1 -1
- data/ext/ffi_c/libffi.mk +1 -1
- data/ext/ffi_c/rbffi.h +1 -0
- data/gen/Rakefile +4 -2
- data/lib/ffi/autopointer.rb +23 -22
- data/lib/ffi/enum.rb +36 -21
- data/lib/ffi/errno.rb +20 -0
- data/lib/ffi/ffi.rb +13 -79
- data/lib/ffi/io.rb +12 -20
- data/lib/ffi/library.rb +110 -93
- data/lib/ffi/managedstruct.rb +1 -1
- data/lib/ffi/memorypointer.rb +15 -21
- data/lib/ffi/platform.rb +24 -28
- data/lib/ffi/pointer.rb +14 -21
- data/lib/ffi/struct.rb +100 -51
- data/lib/ffi/struct_layout_builder.rb +158 -0
- data/lib/ffi/types.rb +99 -128
- data/lib/ffi/union.rb +20 -0
- data/lib/ffi/variadic.rb +33 -22
- data/spec/ffi/async_callback_spec.rb +23 -0
- data/spec/ffi/callback_spec.rb +62 -0
- data/spec/ffi/custom_param_type.rb +31 -0
- data/spec/ffi/custom_type_spec.rb +73 -0
- data/spec/ffi/enum_spec.rb +19 -0
- data/spec/ffi/ffi_spec.rb +24 -0
- data/spec/ffi/pointer_spec.rb +15 -0
- data/spec/ffi/rbx/memory_pointer_spec.rb +7 -1
- data/spec/ffi/strptr_spec.rb +36 -0
- data/spec/ffi/struct_packed_spec.rb +46 -0
- data/spec/ffi/struct_spec.rb +39 -7
- data/spec/ffi/typedef_spec.rb +14 -0
- data/tasks/ann.rake +80 -0
- data/tasks/extension.rake +25 -0
- data/tasks/gem.rake +200 -0
- data/tasks/git.rake +41 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +50 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +301 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- metadata +64 -11
- data/ext/ffi_c/AutoPointer.c +0 -60
- data/ext/ffi_c/AutoPointer.h +0 -18
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
|
2
|
+
|
|
3
|
+
describe "FFI" do
|
|
4
|
+
|
|
5
|
+
describe ".map_library_name" do
|
|
6
|
+
|
|
7
|
+
let(:prefix) { FFI::Platform::LIBPREFIX }
|
|
8
|
+
let(:suffix) { FFI::Platform::LIBSUFFIX }
|
|
9
|
+
|
|
10
|
+
it "should add platform library extension if not present" do
|
|
11
|
+
FFI.map_library_name("#{prefix}dummy").should == "#{prefix}dummy.#{suffix}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "should add platform library extension even if lib suffix is present in name" do
|
|
15
|
+
FFI.map_library_name("#{prefix}dummy_with_#{suffix}").should == "#{prefix}dummy_with_#{suffix}.#{suffix}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should return Platform::LIBC when called with 'c'" do
|
|
19
|
+
FFI.map_library_name('c').should == FFI::Library::LIBC
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
data/spec/ffi/pointer_spec.rb
CHANGED
|
@@ -177,7 +177,21 @@ describe "AutoPointer" do
|
|
|
177
177
|
end
|
|
178
178
|
AutoPointerTestHelper.gc_everything loop_count
|
|
179
179
|
end
|
|
180
|
+
|
|
181
|
+
it "can be used as the return type of a function" do
|
|
182
|
+
lambda do
|
|
183
|
+
Module.new do
|
|
184
|
+
extend FFI::Library
|
|
185
|
+
ffi_lib TestLibrary::PATH
|
|
186
|
+
class CustomAutoPointer < FFI::AutoPointer
|
|
187
|
+
def self.release(ptr); end
|
|
188
|
+
end
|
|
189
|
+
attach_function :ptr_from_address, [ FFI::Platform::ADDRESS_SIZE == 32 ? :uint : :ulong_long ], CustomAutoPointer
|
|
190
|
+
end
|
|
191
|
+
end.should_not raise_error
|
|
192
|
+
end
|
|
180
193
|
end
|
|
194
|
+
|
|
181
195
|
describe "AutoPointer#new" do
|
|
182
196
|
class AutoPointerSubclass < FFI::AutoPointer
|
|
183
197
|
def self.release(ptr); end
|
|
@@ -193,3 +207,4 @@ describe "AutoPointer#new" do
|
|
|
193
207
|
end
|
|
194
208
|
|
|
195
209
|
end
|
|
210
|
+
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# coding: utf-8
|
|
1
2
|
require "rubygems"
|
|
2
3
|
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
|
3
4
|
|
|
@@ -13,7 +14,12 @@ describe "MemoryPointer" do
|
|
|
13
14
|
m = FFI::MemoryPointer.from_string("FFI is Awesome")
|
|
14
15
|
m.type_size.should == 15
|
|
15
16
|
end
|
|
16
|
-
|
|
17
|
+
|
|
18
|
+
it "makes a pointer from a string with multibyte characters" do
|
|
19
|
+
m = FFI::MemoryPointer.from_string("ぱんだ")
|
|
20
|
+
m.type_size.should == 10
|
|
21
|
+
end
|
|
22
|
+
|
|
17
23
|
it "reads back a string" do
|
|
18
24
|
m = FFI::MemoryPointer.from_string("FFI is Awesome")
|
|
19
25
|
m.read_string.should == "FFI is Awesome"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
|
2
|
+
|
|
3
|
+
describe "functions returning :strptr" do
|
|
4
|
+
it "can attach function with :strptr return type" do
|
|
5
|
+
lambda do
|
|
6
|
+
m = Module.new do
|
|
7
|
+
extend FFI::Library
|
|
8
|
+
ffi_lib FFI::Library::LIBC
|
|
9
|
+
attach_function :strdup, [ :string ], :strptr
|
|
10
|
+
end
|
|
11
|
+
end.should_not raise_error
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module StrPtr
|
|
15
|
+
extend FFI::Library
|
|
16
|
+
ffi_lib FFI::Library::LIBC
|
|
17
|
+
attach_function :strdup, [ :string ], :strptr
|
|
18
|
+
attach_function :free, [ :pointer ], :void
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should return [ String, Pointer ]" do
|
|
22
|
+
result = StrPtr.strdup("test")
|
|
23
|
+
result[0].is_a?(String).should be_true
|
|
24
|
+
result[1].is_a?(FFI::Pointer).should be_true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should return the correct value" do
|
|
28
|
+
result = StrPtr.strdup("test")
|
|
29
|
+
result[0].should == "test"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should return non-NULL pointer" do
|
|
33
|
+
result = StrPtr.strdup("test")
|
|
34
|
+
result[1].null?.should be_false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
|
2
|
+
|
|
3
|
+
describe FFI::Struct do
|
|
4
|
+
it "packed :char followed by :int should have size of 5" do
|
|
5
|
+
Class.new(FFI::Struct) do
|
|
6
|
+
packed
|
|
7
|
+
layout :c, :char, :i, :int
|
|
8
|
+
end.size.should == 5
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "packed :char followed by :int should have alignment of 1" do
|
|
12
|
+
Class.new(FFI::Struct) do
|
|
13
|
+
packed
|
|
14
|
+
layout :c, :char, :i, :int
|
|
15
|
+
end.alignment.should == 1
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "packed(2) :char followed by :int should have size of 6" do
|
|
19
|
+
Class.new(FFI::Struct) do
|
|
20
|
+
packed 2
|
|
21
|
+
layout :c, :char, :i, :int
|
|
22
|
+
end.size.should == 6
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "packed(2) :char followed by :int should have alignment of 2" do
|
|
26
|
+
Class.new(FFI::Struct) do
|
|
27
|
+
packed 2
|
|
28
|
+
layout :c, :char, :i, :int
|
|
29
|
+
end.alignment.should == 2
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "packed :short followed by int should have size of 6" do
|
|
33
|
+
Class.new(FFI::Struct) do
|
|
34
|
+
packed
|
|
35
|
+
layout :s, :short, :i, :int
|
|
36
|
+
end.size.should == 6
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "packed :short followed by int should have alignment of 1" do
|
|
40
|
+
Class.new(FFI::Struct) do
|
|
41
|
+
packed
|
|
42
|
+
layout :s, :short, :i, :int
|
|
43
|
+
end.alignment.should == 1
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
data/spec/ffi/struct_spec.rb
CHANGED
|
@@ -133,6 +133,24 @@ describe "Struct tests" do
|
|
|
133
133
|
mp.get_int64(ll_off).should == 0xfee1deadbeef
|
|
134
134
|
end
|
|
135
135
|
end
|
|
136
|
+
|
|
137
|
+
it "subclass overrides initialize without calling super" do
|
|
138
|
+
class InitializeWithoutSuper < FFI::Struct
|
|
139
|
+
layout :a, :int, :b, :long_long, :d, [:double, 2]
|
|
140
|
+
|
|
141
|
+
def initialize(a, b)
|
|
142
|
+
self[:a] = a
|
|
143
|
+
self[:b] = b
|
|
144
|
+
self[:d][0] = 1.2
|
|
145
|
+
self[:d][1] = 3.4
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
end
|
|
149
|
+
s = InitializeWithoutSuper.new(0x1eefbeef, 0xdeadcafebabe)
|
|
150
|
+
s[:a].should == 0x1eefbeef
|
|
151
|
+
s[:b].should == 0xdeadcafebabe
|
|
152
|
+
end
|
|
153
|
+
|
|
136
154
|
it "Can use Struct subclass as parameter type" do
|
|
137
155
|
module StructParam
|
|
138
156
|
extend FFI::Library
|
|
@@ -140,7 +158,7 @@ describe "Struct tests" do
|
|
|
140
158
|
class TestStruct < FFI::Struct
|
|
141
159
|
layout :c, :char
|
|
142
160
|
end
|
|
143
|
-
attach_function :struct_field_s8, [ TestStruct ], :char
|
|
161
|
+
attach_function :struct_field_s8, [ TestStruct.in ], :char
|
|
144
162
|
end
|
|
145
163
|
end
|
|
146
164
|
it "Can use Struct subclass as IN parameter type" do
|
|
@@ -290,14 +308,14 @@ describe "Struct tests" do
|
|
|
290
308
|
extend FFI::Library
|
|
291
309
|
enum :test_enum, [:c1, 10, :c2, 20, :c3, 30, :c4, 40]
|
|
292
310
|
class TestStruct < FFI::Struct
|
|
293
|
-
layout :c, :test_enum
|
|
311
|
+
layout :a, :int, :c, :test_enum
|
|
294
312
|
end
|
|
295
313
|
end
|
|
296
314
|
it ":enum field r/w" do
|
|
297
315
|
s = EnumFields::TestStruct.new
|
|
298
316
|
s[:c] = :c3
|
|
299
317
|
|
|
300
|
-
s.pointer.get_uint(
|
|
318
|
+
s.pointer.get_uint(FFI::Type::INT32.size).should == 30
|
|
301
319
|
s[:c].should == :c3
|
|
302
320
|
end
|
|
303
321
|
module CallbackMember
|
|
@@ -309,8 +327,8 @@ describe "Struct tests" do
|
|
|
309
327
|
layout :add, :add,
|
|
310
328
|
:sub, :sub
|
|
311
329
|
end
|
|
312
|
-
attach_function :struct_call_add_cb, [TestStruct, :int, :int], :int
|
|
313
|
-
attach_function :struct_call_sub_cb, [TestStruct, :int, :int], :int
|
|
330
|
+
attach_function :struct_call_add_cb, [TestStruct.in, :int, :int], :int
|
|
331
|
+
attach_function :struct_call_sub_cb, [TestStruct.in, :int, :int], :int
|
|
314
332
|
end
|
|
315
333
|
it "Can have CallbackInfo struct field" do
|
|
316
334
|
s = CallbackMember::TestStruct.new
|
|
@@ -318,8 +336,8 @@ describe "Struct tests" do
|
|
|
318
336
|
sub_proc = lambda { |a, b| a-b }
|
|
319
337
|
s[:add] = add_proc
|
|
320
338
|
s[:sub] = sub_proc
|
|
321
|
-
CallbackMember.struct_call_add_cb(s
|
|
322
|
-
CallbackMember.struct_call_sub_cb(s
|
|
339
|
+
CallbackMember.struct_call_add_cb(s, 40, 2).should == 42
|
|
340
|
+
CallbackMember.struct_call_sub_cb(s, 44, 2).should == 42
|
|
323
341
|
end
|
|
324
342
|
it "Can return its members as a list" do
|
|
325
343
|
class TestStruct < FFI::Struct
|
|
@@ -603,4 +621,18 @@ describe "Struct allocation" do
|
|
|
603
621
|
p.put_uint(4, 0xdeadbeef)
|
|
604
622
|
S.new(p[1])[:i].should == 0xdeadbeef
|
|
605
623
|
end
|
|
624
|
+
|
|
625
|
+
it "null? should be true when initialized with NULL pointer" do
|
|
626
|
+
class S < FFI::Struct
|
|
627
|
+
layout :i, :uint
|
|
628
|
+
end
|
|
629
|
+
S.new(Pointer::NULL).null?.should be_true
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
it "null? should be false when initialized with non-NULL pointer" do
|
|
633
|
+
class S < FFI::Struct
|
|
634
|
+
layout :i, :uint
|
|
635
|
+
end
|
|
636
|
+
S.new(MemoryPointer.new(S)).null?.should be_false
|
|
637
|
+
end
|
|
606
638
|
end
|
data/spec/ffi/typedef_spec.rb
CHANGED
|
@@ -45,4 +45,18 @@ describe "Custom type definitions" do
|
|
|
45
45
|
s[:a] = 0x12345678
|
|
46
46
|
s.pointer.get_uint(0).should == 0x12345678
|
|
47
47
|
end
|
|
48
|
+
|
|
49
|
+
it "attach_function after a typedef should not reject normal types" do
|
|
50
|
+
lambda do
|
|
51
|
+
Module.new do
|
|
52
|
+
extend FFI::Library
|
|
53
|
+
# enum() will insert a custom typedef called :foo for the enum
|
|
54
|
+
enum :foo, [ :a, :b ]
|
|
55
|
+
typedef :int, :bar
|
|
56
|
+
|
|
57
|
+
ffi_lib TestLibrary::PATH
|
|
58
|
+
attach_function :ptr_ret_int32_t, [ :string, :foo ], :bar
|
|
59
|
+
end
|
|
60
|
+
end.should_not raise_error
|
|
61
|
+
end
|
|
48
62
|
end
|
data/tasks/ann.rake
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
|
|
2
|
+
begin
|
|
3
|
+
require 'bones/smtp_tls'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
require 'net/smtp'
|
|
6
|
+
end
|
|
7
|
+
require 'time'
|
|
8
|
+
|
|
9
|
+
namespace :ann do
|
|
10
|
+
|
|
11
|
+
# A prerequisites task that all other tasks depend upon
|
|
12
|
+
task :prereqs
|
|
13
|
+
|
|
14
|
+
file PROJ.ann.file do
|
|
15
|
+
ann = PROJ.ann
|
|
16
|
+
puts "Generating #{ann.file}"
|
|
17
|
+
File.open(ann.file,'w') do |fd|
|
|
18
|
+
fd.puts("#{PROJ.name} version #{PROJ.version}")
|
|
19
|
+
fd.puts(" by #{Array(PROJ.authors).first}") if PROJ.authors
|
|
20
|
+
fd.puts(" #{PROJ.url}") if PROJ.url.valid?
|
|
21
|
+
fd.puts(" (the \"#{PROJ.release_name}\" release)") if PROJ.release_name
|
|
22
|
+
fd.puts
|
|
23
|
+
fd.puts("== DESCRIPTION")
|
|
24
|
+
fd.puts
|
|
25
|
+
fd.puts(PROJ.description)
|
|
26
|
+
fd.puts
|
|
27
|
+
fd.puts(PROJ.changes.sub(%r/^.*$/, '== CHANGES'))
|
|
28
|
+
fd.puts
|
|
29
|
+
ann.paragraphs.each do |p|
|
|
30
|
+
fd.puts "== #{p.upcase}"
|
|
31
|
+
fd.puts
|
|
32
|
+
fd.puts paragraphs_of(PROJ.readme_file, p).join("\n\n")
|
|
33
|
+
fd.puts
|
|
34
|
+
end
|
|
35
|
+
fd.puts ann.text if ann.text
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
desc "Create an announcement file"
|
|
40
|
+
task :announcement => ['ann:prereqs', PROJ.ann.file]
|
|
41
|
+
|
|
42
|
+
desc "Send an email announcement"
|
|
43
|
+
task :email => ['ann:prereqs', PROJ.ann.file] do
|
|
44
|
+
ann = PROJ.ann
|
|
45
|
+
from = ann.email[:from] || Array(PROJ.authors).first || PROJ.email
|
|
46
|
+
to = Array(ann.email[:to])
|
|
47
|
+
|
|
48
|
+
### build a mail header for RFC 822
|
|
49
|
+
rfc822msg = "From: #{from}\n"
|
|
50
|
+
rfc822msg << "To: #{to.join(',')}\n"
|
|
51
|
+
rfc822msg << "Subject: [ANN] #{PROJ.name} #{PROJ.version}"
|
|
52
|
+
rfc822msg << " (#{PROJ.release_name})" if PROJ.release_name
|
|
53
|
+
rfc822msg << "\n"
|
|
54
|
+
rfc822msg << "Date: #{Time.new.rfc822}\n"
|
|
55
|
+
rfc822msg << "Message-Id: "
|
|
56
|
+
rfc822msg << "<#{"%.8f" % Time.now.to_f}@#{ann.email[:domain]}>\n\n"
|
|
57
|
+
rfc822msg << File.read(ann.file)
|
|
58
|
+
|
|
59
|
+
params = [:server, :port, :domain, :acct, :passwd, :authtype].map do |key|
|
|
60
|
+
ann.email[key]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
params[3] = (PROJ.ann.email[:from] || PROJ.email) if params[3].nil?
|
|
64
|
+
|
|
65
|
+
if params[4].nil?
|
|
66
|
+
STDOUT.write "Please enter your e-mail password (#{params[3]}): "
|
|
67
|
+
params[4] = STDIN.gets.chomp
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
### send email
|
|
71
|
+
Net::SMTP.start(*params) {|smtp| smtp.sendmail(rfc822msg, from, to)}
|
|
72
|
+
end
|
|
73
|
+
end # namespace :ann
|
|
74
|
+
|
|
75
|
+
desc 'Alias to ann:announcement'
|
|
76
|
+
task :ann => 'ann:announcement'
|
|
77
|
+
|
|
78
|
+
CLOBBER << PROJ.ann.file
|
|
79
|
+
|
|
80
|
+
# EOF
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
spec = Gem::Specification.new do |s|
|
|
2
|
+
s.name = PROJ.name
|
|
3
|
+
s.version = PROJ.version
|
|
4
|
+
s.platform = Gem::Platform::RUBY
|
|
5
|
+
s.has_rdoc = true
|
|
6
|
+
s.extra_rdoc_files = ["README.rdoc", "LICENSE"]
|
|
7
|
+
s.summary = PROJ.summary
|
|
8
|
+
s.description = PROJ.description
|
|
9
|
+
s.authors = Array(PROJ.authors)
|
|
10
|
+
s.email = PROJ.email
|
|
11
|
+
s.homepage = Array(PROJ.url).first
|
|
12
|
+
s.rubyforge_project = PROJ.rubyforge.name
|
|
13
|
+
s.extensions = %w(ext/ffi_c/extconf.rb gen/Rakefile)
|
|
14
|
+
s.require_path = 'lib'
|
|
15
|
+
s.files = PROJ.gem.files
|
|
16
|
+
s.add_dependency *PROJ.gem.dependencies.flatten
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Rake::ExtensionTask.new('ffi_c', spec) do |ext|
|
|
20
|
+
ext.name = 'ffi_c' # indicate the name of the extension.
|
|
21
|
+
# ext.lib_dir = BUILD_DIR # put binaries into this folder.
|
|
22
|
+
ext.tmp_dir = BUILD_DIR # temporary folder used during compilation.
|
|
23
|
+
ext.cross_compile = true # enable cross compilation (requires cross compile toolchain)
|
|
24
|
+
ext.cross_platform = ['i386-mingw32', 'i386-mswin32'] # forces the Windows platform instead of the default one
|
|
25
|
+
end if USE_RAKE_COMPILER
|
data/tasks/gem.rake
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
|
|
2
|
+
require 'find'
|
|
3
|
+
require 'rake/packagetask'
|
|
4
|
+
require 'rubygems/user_interaction'
|
|
5
|
+
require 'rubygems/builder'
|
|
6
|
+
|
|
7
|
+
module Bones
|
|
8
|
+
class GemPackageTask < Rake::PackageTask
|
|
9
|
+
# Ruby GEM spec containing the metadata for this package. The
|
|
10
|
+
# name, version and package_files are automatically determined
|
|
11
|
+
# from the GEM spec and don't need to be explicitly provided.
|
|
12
|
+
#
|
|
13
|
+
attr_accessor :gem_spec
|
|
14
|
+
|
|
15
|
+
# Tasks from the Bones gem directory
|
|
16
|
+
attr_reader :bones_files
|
|
17
|
+
|
|
18
|
+
# Create a GEM Package task library. Automatically define the gem
|
|
19
|
+
# if a block is given. If no block is supplied, then +define+
|
|
20
|
+
# needs to be called to define the task.
|
|
21
|
+
#
|
|
22
|
+
def initialize(gem_spec)
|
|
23
|
+
init(gem_spec)
|
|
24
|
+
yield self if block_given?
|
|
25
|
+
define if block_given?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Initialization tasks without the "yield self" or define
|
|
29
|
+
# operations.
|
|
30
|
+
#
|
|
31
|
+
def init(gem)
|
|
32
|
+
super(gem.name, gem.version)
|
|
33
|
+
@gem_spec = gem
|
|
34
|
+
@package_files += gem_spec.files if gem_spec.files
|
|
35
|
+
@bones_files = []
|
|
36
|
+
|
|
37
|
+
local_setup = File.join(Dir.pwd, %w[tasks setup.rb])
|
|
38
|
+
if !test(?e, local_setup)
|
|
39
|
+
Dir.glob(::Bones.path(%w[lib bones tasks *])).each {|fn| bones_files << fn}
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Create the Rake tasks and actions specified by this
|
|
44
|
+
# GemPackageTask. (+define+ is automatically called if a block is
|
|
45
|
+
# given to +new+).
|
|
46
|
+
#
|
|
47
|
+
def define
|
|
48
|
+
super
|
|
49
|
+
task :prereqs
|
|
50
|
+
task :package => ['gem:prereqs', "#{package_dir_path}/#{gem_file}"]
|
|
51
|
+
file "#{package_dir_path}/#{gem_file}" => [package_dir_path] + package_files + bones_files do
|
|
52
|
+
when_writing("Creating GEM") {
|
|
53
|
+
chdir(package_dir_path) do
|
|
54
|
+
Gem::Builder.new(gem_spec).build
|
|
55
|
+
verbose(true) {
|
|
56
|
+
mv gem_file, "../#{gem_file}"
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
file package_dir_path => bones_files do
|
|
63
|
+
mkdir_p package_dir rescue nil
|
|
64
|
+
|
|
65
|
+
gem_spec.files = (gem_spec.files +
|
|
66
|
+
bones_files.map {|fn| File.join('tasks', File.basename(fn))}).sort
|
|
67
|
+
|
|
68
|
+
bones_files.each do |fn|
|
|
69
|
+
base_fn = File.join('tasks', File.basename(fn))
|
|
70
|
+
f = File.join(package_dir_path, base_fn)
|
|
71
|
+
fdir = File.dirname(f)
|
|
72
|
+
mkdir_p(fdir) if !File.exist?(fdir)
|
|
73
|
+
if File.directory?(fn)
|
|
74
|
+
mkdir_p(f)
|
|
75
|
+
else
|
|
76
|
+
raise "file name conflict for '#{base_fn}' (conflicts with '#{fn}')" if test(?e, f)
|
|
77
|
+
safe_ln(fn, f)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def gem_file
|
|
84
|
+
if @gem_spec.platform == Gem::Platform::RUBY
|
|
85
|
+
"#{package_name}.gem"
|
|
86
|
+
else
|
|
87
|
+
"#{package_name}-#{@gem_spec.platform}.gem"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end # class GemPackageTask
|
|
91
|
+
end # module Bones
|
|
92
|
+
|
|
93
|
+
namespace :gem do
|
|
94
|
+
|
|
95
|
+
PROJ.gem._spec = Gem::Specification.new do |s|
|
|
96
|
+
s.name = PROJ.name
|
|
97
|
+
s.version = PROJ.version
|
|
98
|
+
s.summary = PROJ.summary
|
|
99
|
+
s.authors = Array(PROJ.authors)
|
|
100
|
+
s.email = PROJ.email
|
|
101
|
+
s.homepage = Array(PROJ.url).first
|
|
102
|
+
s.rubyforge_project = PROJ.rubyforge.name
|
|
103
|
+
|
|
104
|
+
s.description = PROJ.description
|
|
105
|
+
|
|
106
|
+
PROJ.gem.dependencies.each do |dep|
|
|
107
|
+
s.add_dependency(*dep)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
PROJ.gem.development_dependencies.each do |dep|
|
|
111
|
+
s.add_development_dependency(*dep)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
s.files = PROJ.gem.files
|
|
115
|
+
s.executables = PROJ.gem.executables.map {|fn| File.basename(fn)}
|
|
116
|
+
s.extensions = PROJ.gem.files.grep %r/extconf\.rb$/
|
|
117
|
+
|
|
118
|
+
s.bindir = 'bin'
|
|
119
|
+
dirs = Dir["{#{PROJ.libs.join(',')}}"]
|
|
120
|
+
s.require_paths = dirs unless dirs.empty?
|
|
121
|
+
|
|
122
|
+
incl = Regexp.new(PROJ.rdoc.include.join('|'))
|
|
123
|
+
excl = PROJ.rdoc.exclude.dup.concat %w[\.rb$ ^(\.\/|\/)?ext]
|
|
124
|
+
excl = Regexp.new(excl.join('|'))
|
|
125
|
+
rdoc_files = PROJ.gem.files.find_all do |fn|
|
|
126
|
+
case fn
|
|
127
|
+
when excl; false
|
|
128
|
+
when incl; true
|
|
129
|
+
else false end
|
|
130
|
+
end
|
|
131
|
+
s.rdoc_options = PROJ.rdoc.opts + ['--main', PROJ.rdoc.main]
|
|
132
|
+
s.extra_rdoc_files = rdoc_files
|
|
133
|
+
s.has_rdoc = true
|
|
134
|
+
|
|
135
|
+
if test ?f, PROJ.test.file
|
|
136
|
+
s.test_file = PROJ.test.file
|
|
137
|
+
else
|
|
138
|
+
s.test_files = PROJ.test.files.to_a
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Do any extra stuff the user wants
|
|
142
|
+
PROJ.gem.extras.each do |msg, val|
|
|
143
|
+
case val
|
|
144
|
+
when Proc
|
|
145
|
+
val.call(s.send(msg))
|
|
146
|
+
else
|
|
147
|
+
s.send "#{msg}=", val
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end # Gem::Specification.new
|
|
151
|
+
|
|
152
|
+
Bones::GemPackageTask.new(PROJ.gem._spec) do |pkg|
|
|
153
|
+
pkg.need_tar = PROJ.gem.need_tar
|
|
154
|
+
pkg.need_zip = PROJ.gem.need_zip
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
desc 'Show information about the gem'
|
|
158
|
+
task :debug => 'gem:prereqs' do
|
|
159
|
+
puts PROJ.gem._spec.to_ruby
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
desc 'Write the gemspec '
|
|
163
|
+
task :spec => 'gem:prereqs' do
|
|
164
|
+
File.open("#{PROJ.name}.gemspec", 'w') do |f|
|
|
165
|
+
f.write PROJ.gem._spec.to_ruby
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
desc 'Install the gem'
|
|
170
|
+
task :install => [:clobber, 'gem:package'] do
|
|
171
|
+
sh "#{SUDO} #{GEM} install --local pkg/#{PROJ.gem._spec.full_name}"
|
|
172
|
+
|
|
173
|
+
# use this version of the command for rubygems > 1.0.0
|
|
174
|
+
#sh "#{SUDO} #{GEM} install --no-update-sources pkg/#{PROJ.gem._spec.full_name}"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
desc 'Uninstall the gem'
|
|
178
|
+
task :uninstall do
|
|
179
|
+
installed_list = Gem.source_index.find_name(PROJ.name)
|
|
180
|
+
if installed_list and installed_list.collect { |s| s.version.to_s}.include?(PROJ.version) then
|
|
181
|
+
sh "#{SUDO} #{GEM} uninstall --version '#{PROJ.version}' --ignore-dependencies --executables #{PROJ.name}"
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
desc 'Reinstall the gem'
|
|
186
|
+
task :reinstall => [:uninstall, :install]
|
|
187
|
+
|
|
188
|
+
desc 'Cleanup the gem'
|
|
189
|
+
task :cleanup do
|
|
190
|
+
sh "#{SUDO} #{GEM} cleanup #{PROJ.gem._spec.name}"
|
|
191
|
+
end
|
|
192
|
+
end # namespace :gem
|
|
193
|
+
|
|
194
|
+
desc 'Alias to gem:package'
|
|
195
|
+
task :gem => 'gem:package'
|
|
196
|
+
|
|
197
|
+
task :clobber => 'gem:clobber_package'
|
|
198
|
+
remove_desc_for_task 'gem:clobber_package'
|
|
199
|
+
|
|
200
|
+
# EOF
|
data/tasks/git.rake
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
|
|
2
|
+
if HAVE_GIT
|
|
3
|
+
|
|
4
|
+
namespace :git do
|
|
5
|
+
|
|
6
|
+
# A prerequisites task that all other tasks depend upon
|
|
7
|
+
task :prereqs
|
|
8
|
+
|
|
9
|
+
desc 'Show tags from the Git repository'
|
|
10
|
+
task :show_tags => 'git:prereqs' do |t|
|
|
11
|
+
puts %x/git tag/
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
desc 'Create a new tag in the Git repository'
|
|
15
|
+
task :create_tag => 'git:prereqs' do |t|
|
|
16
|
+
v = ENV['VERSION'] or abort 'Must supply VERSION=x.y.z'
|
|
17
|
+
abort "Versions don't match #{v} vs #{PROJ.version}" if v != PROJ.version
|
|
18
|
+
|
|
19
|
+
# tag = "%s-%s" % [PROJ.name, PROJ.version]
|
|
20
|
+
tag = "%s" % [ PROJ.version ]
|
|
21
|
+
msg = "Creating tag for #{PROJ.name} version #{PROJ.version}"
|
|
22
|
+
|
|
23
|
+
puts "Creating Git tag '#{tag}'"
|
|
24
|
+
unless system "git tag -a -m '#{msg}' #{tag}"
|
|
25
|
+
abort "Tag creation failed"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# if %x/git remote/ =~ %r/^origin\s*$/
|
|
29
|
+
# unless system "git push origin #{tag}"
|
|
30
|
+
# abort "Could not push tag to remote Git repository"
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end # namespace :git
|
|
36
|
+
|
|
37
|
+
#task 'gem:release' => 'git:create_tag'
|
|
38
|
+
|
|
39
|
+
end # if HAVE_GIT
|
|
40
|
+
|
|
41
|
+
# EOF
|
data/tasks/notes.rake
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
if HAVE_BONES
|
|
3
|
+
|
|
4
|
+
desc "Enumerate all annotations"
|
|
5
|
+
task :notes do |t|
|
|
6
|
+
id = if t.application.top_level_tasks.length > 1
|
|
7
|
+
t.application.top_level_tasks.slice!(1..-1).join(' ')
|
|
8
|
+
end
|
|
9
|
+
Bones::AnnotationExtractor.enumerate(
|
|
10
|
+
PROJ, PROJ.notes.tags.join('|'), id, :tag => true)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
namespace :notes do
|
|
14
|
+
PROJ.notes.tags.each do |tag|
|
|
15
|
+
desc "Enumerate all #{tag} annotations"
|
|
16
|
+
task tag.downcase.to_sym do |t|
|
|
17
|
+
id = if t.application.top_level_tasks.length > 1
|
|
18
|
+
t.application.top_level_tasks.slice!(1..-1).join(' ')
|
|
19
|
+
end
|
|
20
|
+
Bones::AnnotationExtractor.enumerate(PROJ, tag, id)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end # if HAVE_BONES
|
|
26
|
+
|
|
27
|
+
# EOF
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
# This file does not define any rake tasks. It is used to load some project
|
|
3
|
+
# settings if they are not defined by the user.
|
|
4
|
+
|
|
5
|
+
PROJ.exclude << ["^#{Regexp.escape(PROJ.ann.file)}$",
|
|
6
|
+
"^#{Regexp.escape(PROJ.ignore_file)}$",
|
|
7
|
+
"^#{Regexp.escape(PROJ.rdoc.dir)}/",
|
|
8
|
+
"^#{Regexp.escape(PROJ.rcov.dir)}/"]
|
|
9
|
+
|
|
10
|
+
flatten_arrays = lambda do |this,os|
|
|
11
|
+
os.instance_variable_get(:@table).each do |key,val|
|
|
12
|
+
next if key == :dependencies \
|
|
13
|
+
or key == :development_dependencies
|
|
14
|
+
case val
|
|
15
|
+
when Array; val.flatten!
|
|
16
|
+
when OpenStruct; this.call(this,val)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
flatten_arrays.call(flatten_arrays,PROJ)
|
|
21
|
+
|
|
22
|
+
PROJ.changes ||= paragraphs_of(PROJ.history_file, 0..1).join("\n\n")
|
|
23
|
+
|
|
24
|
+
PROJ.description ||= paragraphs_of(PROJ.readme_file, 'description').join("\n\n")
|
|
25
|
+
|
|
26
|
+
PROJ.summary ||= PROJ.description.split('.').first
|
|
27
|
+
|
|
28
|
+
PROJ.gem.files ||= manifest
|
|
29
|
+
|
|
30
|
+
PROJ.gem.executables ||= PROJ.gem.files.find_all {|fn| fn =~ %r/^bin/}
|
|
31
|
+
|
|
32
|
+
PROJ.rdoc.main ||= PROJ.readme_file
|
|
33
|
+
|
|
34
|
+
# EOF
|