luislavena-rake-compiler 0.1.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.
@@ -0,0 +1,25 @@
1
+ Given /^a gem named '(.*)'$/ do |gem_name|
2
+ generate_gem_task gem_name
3
+ end
4
+
5
+ Then /^ruby gem for '(.*)' version '(.*)' do exist in '(.*)'$/ do |name, version, folder|
6
+ File.exist?(gem_file(folder, name, version)).should be_true
7
+ end
8
+
9
+ Then /^binary gem for '(.*)' version '(.*)' do exist in '(.*)'$/ do |name, version, folder|
10
+ File.exist?(gem_file_platform(folder, name, version)).should be_true
11
+ end
12
+
13
+ Then /^a gem for '(.*)' version '(.*)' platform '(.*)' do exist in '(.*)'$/ do |name, version, platform, folder|
14
+ File.exist?(gem_file_platform(folder, name, version, platform)).should be_true
15
+ end
16
+
17
+ def gem_file(folder, name, version)
18
+ "#{folder}/#{name}-#{version}.gem"
19
+ end
20
+
21
+ def gem_file_platform(folder, name, version, platform = nil)
22
+ file = "#{folder}/#{name}-#{version}"
23
+ file << "-" << (platform || Gem::Platform.local.to_s)
24
+ file << ".gem"
25
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'cucumber'
3
+ require 'spec'
4
+ require 'fileutils'
5
+ require 'rbconfig'
6
+
7
+ ROOT_PATH = File.expand_path(File.join(File.dirname(__FILE__), '../..'))
@@ -0,0 +1,75 @@
1
+ def template_rakefile
2
+ <<-EOF
3
+ # add rake-compiler lib dir to the LOAD_PATH
4
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), '../..', 'lib'))
5
+
6
+ require 'rubygems'
7
+ require 'rake'
8
+
9
+ # load rakefile extensions (tasks)
10
+ Dir['tasks/*.rake'].each { |f| import f }
11
+ EOF
12
+ end
13
+
14
+ def template_rake_gemspec(gem_name)
15
+ <<-EOF
16
+ require 'rake/gempackagetask'
17
+ SPEC = Gem::Specification.new do |s|
18
+ s.name = "#{gem_name}"
19
+ s.version = "0.1.0"
20
+ s.summary = "#{gem_name} test gem for rake-compiler"
21
+
22
+ s.has_rdoc = true
23
+
24
+ s.homepage = 'http://github.com/luislavena/rake-compiler'
25
+ s.rubyforge_project = 'TODO'
26
+
27
+ s.authors = ["Luis Lavena"]
28
+ s.email = ["luislavena@gmail.com"]
29
+ end
30
+
31
+ gem_package = Rake::GemPackageTask.new(SPEC) do |pkg|
32
+ pkg.need_zip = false
33
+ pkg.need_tar = false
34
+ end
35
+ EOF
36
+ end
37
+
38
+ def template_rake_extension(extension_name, gem_spec = nil)
39
+ <<-EOF
40
+ require 'rake/extensiontask'
41
+ Rake::ExtensionTask.new("#{extension_name}"#{', SPEC' if gem_spec})
42
+ EOF
43
+ end
44
+
45
+ def template_rake_extension_with_platform(extension_name, platform)
46
+ <<-EOF
47
+ require 'rake/extensiontask'
48
+ Rake::ExtensionTask.new("#{extension_name}", SPEC) do |ext|
49
+ ext.platform = "#{platform}"
50
+ end
51
+ EOF
52
+ end
53
+
54
+ def template_extconf(extension_name)
55
+ <<-EOF
56
+ require 'mkmf'
57
+ create_makefile("#{extension_name}")
58
+ EOF
59
+ end
60
+
61
+ def template_source_c(extension_name)
62
+ <<-EOF
63
+ #include "source.h"
64
+ void Init_#{extension_name}()
65
+ {
66
+ printf("source.c of extension #{extension_name}\\n");
67
+ }
68
+ EOF
69
+ end
70
+
71
+ def template_source_h
72
+ <<-EOF
73
+ #include "ruby.h"
74
+ EOF
75
+ end
@@ -0,0 +1,58 @@
1
+ def generate_scaffold_structure
2
+ # create folder structure
3
+ FileUtils.mkdir_p "lib"
4
+ FileUtils.mkdir_p "tasks"
5
+ FileUtils.mkdir_p "tmp"
6
+
7
+ # create Rakefile loader
8
+ File.open("Rakefile", 'w') do |rakefile|
9
+ rakefile.puts template_rakefile.strip
10
+ end
11
+ end
12
+
13
+ def generate_gem_task(gem_name)
14
+ # create generic gem task
15
+ File.open("tasks/gem.rake", 'w') do |gem_rake|
16
+ gem_rake.puts template_rake_gemspec(gem_name)
17
+ end
18
+ end
19
+
20
+ def generate_extension_task_for(extension_name, platform = nil)
21
+ # create folder structure
22
+ FileUtils.mkdir_p "ext/#{extension_name}"
23
+
24
+ return if File.exist?("tasks/#{extension_name}.rake")
25
+
26
+ # Building a gem?
27
+ if File.exist?("tasks/gem.rake") then
28
+ File.open("tasks/gem.rake", 'a+') do |ext_in_gem|
29
+ if platform
30
+ ext_in_gem.puts template_rake_extension_with_platform(extension_name, platform)
31
+ else
32
+ ext_in_gem.puts template_rake_extension(extension_name, true)
33
+ end
34
+ end
35
+ else
36
+ # create specific extension rakefile
37
+ File.open("tasks/#{extension_name}.rake", 'w') do |ext_rake|
38
+ ext_rake.puts template_rake_extension(extension_name)
39
+ end
40
+ end
41
+ end
42
+
43
+ def generate_source_code_for(extension_name)
44
+ # source C file
45
+ File.open("ext/#{extension_name}/source.c", 'w') do |c|
46
+ c.puts template_source_c(extension_name)
47
+ end
48
+
49
+ # header H file
50
+ File.open("ext/#{extension_name}/source.h", 'w') do |h|
51
+ h.puts template_source_h
52
+ end
53
+
54
+ # extconf.rb file
55
+ File.open("ext/#{extension_name}/extconf.rb", 'w') do |ext|
56
+ ext.puts template_extconf(extension_name)
57
+ end
58
+ end
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Define a series of tasks to aid in the compilation of C extensions for
4
+ # gem developer/creators.
5
+
6
+ require 'rake'
7
+ require 'rake/clean'
8
+ require 'rake/tasklib'
9
+ require 'rbconfig'
10
+
11
+ module Rake
12
+ class ExtensionTask < TaskLib
13
+ attr_accessor :name
14
+ attr_accessor :gem_spec
15
+ attr_accessor :config_script
16
+ attr_accessor :tmp_dir
17
+ attr_accessor :ext_dir
18
+ attr_accessor :lib_dir
19
+ attr_accessor :platform
20
+ attr_accessor :additional_options
21
+ attr_accessor :source_pattern
22
+
23
+ def initialize(name = nil, gem_spec = nil)
24
+ init(name, gem_spec)
25
+ yield self if block_given?
26
+ define
27
+ end
28
+
29
+ def init(name = nil, gem_spec = nil)
30
+ @name = name
31
+ @gem_spec = gem_spec
32
+ @config_script = 'extconf.rb'
33
+ @tmp_dir = 'tmp'
34
+ @ext_dir = 'ext'
35
+ @lib_dir = 'lib'
36
+ @source_pattern = "*.c"
37
+ @additional_options = []
38
+ end
39
+
40
+ def platform
41
+ @platform ||= RUBY_PLATFORM
42
+ end
43
+
44
+ def define
45
+ fail "Extension name must be provided." if @name.nil?
46
+
47
+ define_compile_tasks
48
+ define_native_tasks if @gem_spec
49
+ end
50
+
51
+ private
52
+ def define_compile_tasks
53
+ # directories we need
54
+ directory tmp_path
55
+ directory @lib_dir
56
+
57
+ # platform specific temp folder should be on the cleaning
58
+ CLEAN.include(tmp_path)
59
+
60
+ # makefile depends of tmp_dir and config_script
61
+ # tmp/extension_name/Makefile
62
+ file makefile => [tmp_path, extconf] do
63
+ parent = Dir.pwd
64
+ Dir.chdir tmp_path do
65
+ # FIXME: Rake is broken for multiple arguments system() calls.
66
+ # Add current directory to the search path of Ruby
67
+ # Also, include additional parameters supplied.
68
+ ruby ['-I.', File.join(parent, extconf), *@additional_options].join(' ')
69
+ end
70
+ end
71
+
72
+ # binary in temporary folder depends on makefile and source files
73
+ # tmp/extension_name/extension_name.{so,bundle}
74
+ file tmp_binary => [makefile] + source_files do
75
+ Dir.chdir tmp_path do
76
+ sh make
77
+ end
78
+ end
79
+
80
+ # copy binary from temporary location to final lib
81
+ # tmp/extension_name/extension_name.{so,bundle} => lib/
82
+ file lib_binary => [lib_path, tmp_binary] do
83
+ cp tmp_binary, lib_binary
84
+ end
85
+
86
+ # clobbering should remove the binaries from lib_path
87
+ CLOBBER.include(lib_binary)
88
+
89
+ # we should also clobber the tmp folder
90
+ CLOBBER.include(@tmp_dir)
91
+
92
+ desc "Compile just the #{@name} extension"
93
+ task "compile:#{@name}" => [lib_binary]
94
+
95
+ desc "Compile the extension(s)" unless Rake::Task.task_defined?('compile')
96
+ task "compile" => ["compile:#{@name}"]
97
+ end
98
+
99
+ def define_native_tasks
100
+ # only gems with 'ruby' platforms are allowed to define native tasks
101
+ return unless @gem_spec.platform == 'ruby'
102
+
103
+ require 'rake/gempackagetask' unless defined?(Rake::GemPackageTask)
104
+
105
+ # create 'native:gem_name' and chain it to 'native' task
106
+ native_task_for(@gem_spec)
107
+
108
+ # hook the binary to the prerequisites for this task
109
+ task native_task_gem => [lib_binary]
110
+ end
111
+
112
+ def makefile
113
+ "#{tmp_path}/Makefile"
114
+ end
115
+
116
+ def extconf
117
+ "#{ext_path}/#{@config_script}"
118
+ end
119
+
120
+ def tmp_path
121
+ File.join(@tmp_dir, platform, @name)
122
+ end
123
+
124
+ def ext_path
125
+ File.join(@ext_dir, @name)
126
+ end
127
+
128
+ def lib_path
129
+ @lib_dir
130
+ end
131
+
132
+ def make
133
+ RUBY_PLATFORM =~ /mswin/ ? 'nmake' : 'make'
134
+ end
135
+
136
+ def binary
137
+ "#{@name}.#{RbConfig::CONFIG['DLEXT']}"
138
+ end
139
+
140
+ def source_files
141
+ @source_files ||= FileList["#{ext_path}/#{@source_pattern}"]
142
+ end
143
+
144
+ def tmp_binary
145
+ "#{tmp_path}/#{binary}"
146
+ end
147
+
148
+ def lib_binary
149
+ "#{lib_path}/#{binary}"
150
+ end
151
+
152
+ def native_task_gem
153
+ "native:#{@gem_spec.name}"
154
+ end
155
+
156
+ def native_task_for(gem_spec)
157
+ return if Rake::Task.task_defined?(native_task_gem)
158
+
159
+ spec = gem_spec.dup
160
+
161
+ task native_task_gem do |t|
162
+ # adjust to current platform
163
+ spec.platform = (@platform || Gem::Platform::CURRENT)
164
+
165
+ # clear the extensions defined in the specs
166
+ spec.extensions.clear
167
+
168
+ # add the binary dependencies of this task
169
+ spec.files += t.prerequisites
170
+
171
+ # Generate a package for this gem
172
+ gem_package = Rake::GemPackageTask.new(spec) do |pkg|
173
+ pkg.need_zip = false
174
+ pkg.need_tar = false
175
+ end
176
+ end
177
+
178
+ # add this native task to the list
179
+ task "native" => [native_task_gem]
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,245 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ require 'rake/extensiontask'
4
+ require 'rbconfig'
5
+
6
+ describe Rake::ExtensionTask do
7
+ describe '#new' do
8
+ describe '(basic)' do
9
+ it 'should raise an error if no name is provided' do
10
+ lambda {
11
+ Rake::ExtensionTask.new
12
+ }.should raise_error(RuntimeError, /Extension name must be provided/)
13
+ end
14
+
15
+ it 'should allow string as extension name assignation' do
16
+ ext = Rake::ExtensionTask.new('extension_one')
17
+ ext.name.should == 'extension_one'
18
+ end
19
+
20
+ it 'should allow string as extension name using block assignation' do
21
+ ext = Rake::ExtensionTask.new do |ext|
22
+ ext.name = 'extension_two'
23
+ end
24
+ ext.name.should == 'extension_two'
25
+ end
26
+
27
+ it 'should return itself for the block' do
28
+ from_block = nil
29
+ from_lasgn = Rake::ExtensionTask.new('extension_three') do |ext|
30
+ from_block = ext
31
+ end
32
+ from_block.should == from_lasgn
33
+ end
34
+
35
+ it 'should accept a gem specification as parameter' do
36
+ spec = mock_gem_spec
37
+ ext = Rake::ExtensionTask.new('extension_three', spec)
38
+ ext.gem_spec.should == spec
39
+ end
40
+
41
+ it 'should allow gem specification be defined using block assignation' do
42
+ spec = mock_gem_spec
43
+ ext = Rake::ExtensionTask.new('extension_four') do |ext|
44
+ ext.gem_spec = spec
45
+ end
46
+ ext.gem_spec.should == spec
47
+ end
48
+
49
+ it 'should allow forcing of platform' do
50
+ ext = Rake::ExtensionTask.new('weird_extension') do |ext|
51
+ ext.platform = 'universal-foo-bar-10.5'
52
+ end
53
+ ext.platform.should == 'universal-foo-bar-10.5'
54
+ end
55
+ end
56
+ end
57
+
58
+ describe '(defaults)' do
59
+ before :each do
60
+ @ext = Rake::ExtensionTask.new('extension_one')
61
+ end
62
+
63
+ it 'should look for extconf script' do
64
+ @ext.config_script.should == 'extconf.rb'
65
+ end
66
+
67
+ it 'should dump intermediate files to tmp/' do
68
+ @ext.tmp_dir.should == 'tmp'
69
+ end
70
+
71
+ it 'should look for extension inside ext/' do
72
+ @ext.ext_dir.should == 'ext'
73
+ end
74
+
75
+ it 'should copy build extension into lib/' do
76
+ @ext.lib_dir.should == 'lib'
77
+ end
78
+
79
+ it 'should look for C files pattern (.c)' do
80
+ @ext.source_pattern.should == "*.c"
81
+ end
82
+
83
+ it 'should have no additional options preset to delegate' do
84
+ @ext.additional_options.should be_empty
85
+ end
86
+
87
+ it 'should default to current platform' do
88
+ @ext.platform.should == RUBY_PLATFORM
89
+ end
90
+ end
91
+
92
+ describe '(tasks)' do
93
+ before :each do
94
+ Rake.application.clear
95
+ CLEAN.clear
96
+ CLOBBER.clear
97
+ end
98
+
99
+ describe '(one extension)' do
100
+ before :each do
101
+ Rake::FileList.stub!(:[]).and_return(["ext/extension_one/source.c"])
102
+ @ext = Rake::ExtensionTask.new('extension_one')
103
+ @ext_bin = ext_bin('extension_one')
104
+ @platform = RUBY_PLATFORM
105
+ end
106
+
107
+ describe 'compile' do
108
+ it 'should define as task' do
109
+ Rake::Task.task_defined?('compile').should be_true
110
+ end
111
+
112
+ it "should depend on 'compile:extension_one'" do
113
+ Rake::Task['compile'].prerequisites.should include('compile:extension_one')
114
+ end
115
+ end
116
+
117
+ describe 'compile:extension_one' do
118
+ it 'should define as task' do
119
+ Rake::Task.task_defined?('compile:extension_one').should be_true
120
+ end
121
+
122
+ it "should depend on 'lib/extension_one.{so,bundle}'" do
123
+ Rake::Task['compile:extension_one'].prerequisites.should include("lib/#{@ext_bin}")
124
+ end
125
+ end
126
+
127
+ describe 'lib/extension_one.{so,bundle}' do
128
+ it 'should define as task' do
129
+ Rake::Task.task_defined?("lib/#{@ext_bin}").should be_true
130
+ end
131
+
132
+ it "should depend on 'lib'" do
133
+ Rake::Task["lib/#{@ext_bin}"].prerequisites.should include("lib")
134
+ end
135
+
136
+ it "should depend on 'tmp/extension_one.{so,bundle}'" do
137
+ Rake::Task["lib/#{@ext_bin}"].prerequisites.should include("tmp/#{@platform}/extension_one/#{@ext_bin}")
138
+ end
139
+ end
140
+
141
+ describe 'tmp/{platform}/extension_one/extension_one.{so,bundle}' do
142
+ it 'should define as task' do
143
+ Rake::Task.task_defined?("tmp/#{@platform}/extension_one/#{@ext_bin}").should be_true
144
+ end
145
+
146
+ it "should depend on 'tmp/{platform}/extension_one/Makefile'" do
147
+ Rake::Task["tmp/#{@platform}/extension_one/#{@ext_bin}"].prerequisites.should include("tmp/#{@platform}/extension_one/Makefile")
148
+ end
149
+
150
+ it "should depend on 'ext/extension_one/source.c'" do
151
+ Rake::Task["tmp/#{@platform}/extension_one/#{@ext_bin}"].prerequisites.should include("ext/extension_one/source.c")
152
+ end
153
+
154
+ it "should not depend on 'ext/extension_one/source.h'" do
155
+ Rake::Task["tmp/#{@platform}/extension_one/#{@ext_bin}"].prerequisites.should_not include("ext/extension_one/source.h")
156
+ end
157
+ end
158
+
159
+ describe 'tmp/{platform}/extension_one/Makefile' do
160
+ it 'should define as task' do
161
+ Rake::Task.task_defined?("tmp/#{@platform}/extension_one/Makefile").should be_true
162
+ end
163
+
164
+ it "should depend on 'tmp/{platform}/extension_one'" do
165
+ Rake::Task["tmp/#{@platform}/extension_one/Makefile"].prerequisites.should include("tmp/#{@platform}/extension_one")
166
+ end
167
+
168
+ it "should depend on 'ext/extension_one/extconf.rb'" do
169
+ Rake::Task["tmp/#{@platform}/extension_one/Makefile"].prerequisites.should include("ext/extension_one/extconf.rb")
170
+ end
171
+ end
172
+
173
+ describe 'clean' do
174
+ it "should include 'tmp/{platform}/extension_one' in the pattern" do
175
+ CLEAN.should include("tmp/#{@platform}/extension_one")
176
+ end
177
+ end
178
+
179
+ describe 'clobber' do
180
+ it "should include 'lib/extension_one.{so,bundle}'" do
181
+ CLOBBER.should include("lib/#{@ext_bin}")
182
+ end
183
+
184
+ it "should include 'tmp'" do
185
+ CLOBBER.should include('tmp')
186
+ end
187
+ end
188
+ end
189
+
190
+ describe '(native tasks)' do
191
+ before :each do
192
+ Rake::FileList.stub!(:[]).and_return(["ext/extension_one/source.c"])
193
+ @spec = mock_gem_spec
194
+ @ext_bin = ext_bin('extension_one')
195
+ end
196
+
197
+ describe 'native' do
198
+ before :each do
199
+ @spec.stub!(:platform=).and_return('ruby')
200
+ end
201
+
202
+ it 'should define a task for building the supplied gem' do
203
+ Rake::ExtensionTask.new('extension_one', @spec)
204
+ Rake::Task.task_defined?('native:my_gem').should be_true
205
+ end
206
+
207
+ it 'should define as task for pure ruby gems' do
208
+ Rake::Task.task_defined?('native').should be_false
209
+ Rake::ExtensionTask.new('extension_one', @spec)
210
+ Rake::Task.task_defined?('native').should be_true
211
+ end
212
+
213
+ it 'should not define a task for already native gems' do
214
+ @spec.stub!(:platform).and_return('current')
215
+ Rake::ExtensionTask.new('extension_one', @spec)
216
+ Rake::Task.task_defined?('native').should be_false
217
+ end
218
+
219
+ it 'should depend on gem specific native tasks' do
220
+ Rake::ExtensionTask.new('extension_one', @spec)
221
+ Rake::Task["native"].prerequisites.should include("native:my_gem")
222
+ end
223
+
224
+ describe 'native:my_gem' do
225
+ it 'should depend on binary extension' do
226
+ Rake::ExtensionTask.new('extension_one', @spec)
227
+ Rake::Task["native:my_gem"].prerequisites.should include("lib/#{@ext_bin}")
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ private
235
+ def ext_bin(extension_name)
236
+ "#{extension_name}.#{RbConfig::CONFIG['DLEXT']}"
237
+ end
238
+
239
+ def mock_gem_spec(stubs = {})
240
+ mock(Gem::Specification,
241
+ { :name => 'my_gem', :platform => 'ruby' }.merge(stubs)
242
+ )
243
+ end
244
+
245
+ end