rake-compiler 0.3.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 +32 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +258 -0
- data/Rakefile +23 -0
- data/bin/rake-compiler +24 -0
- data/cucumber.yml +1 -0
- data/features/compile.feature +71 -0
- data/features/cross-compile.feature +13 -0
- data/features/cross-package.feature +14 -0
- data/features/package.feature +40 -0
- data/features/step_definitions/compilation.rb +43 -0
- data/features/step_definitions/cross_compilation.rb +21 -0
- data/features/step_definitions/execution.rb +30 -0
- data/features/step_definitions/folders.rb +32 -0
- data/features/step_definitions/gem.rb +29 -0
- data/features/support/env.rb +7 -0
- data/features/support/file_templates.rb +88 -0
- data/features/support/generators.rb +77 -0
- data/lib/rake/extensiontask.rb +270 -0
- data/spec/lib/rake/extensiontask_spec.rb +352 -0
- data/spec/spec_helper.rb +9 -0
- data/tasks/bin/cross-ruby.rake +159 -0
- data/tasks/common.rake +13 -0
- data/tasks/cucumber.rake +14 -0
- data/tasks/gem.rake +59 -0
- data/tasks/rdoc.rake +9 -0
- data/tasks/release.rake +79 -0
- data/tasks/rspec.rake +33 -0
- metadata +97 -0
@@ -0,0 +1,88 @@
|
|
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.files = FileList["ext/**/*.{rb,c,h}", "Rakefile", "tasks/*.rake", "lib/**/*.rb"]
|
23
|
+
|
24
|
+
s.extensions = FileList["ext/**/extconf.rb"]
|
25
|
+
|
26
|
+
s.has_rdoc = true
|
27
|
+
|
28
|
+
s.homepage = 'http://github.com/luislavena/rake-compiler'
|
29
|
+
s.rubyforge_project = 'TODO'
|
30
|
+
|
31
|
+
s.authors = ["Luis Lavena"]
|
32
|
+
s.email = ["luislavena@gmail.com"]
|
33
|
+
end
|
34
|
+
|
35
|
+
gem_package = Rake::GemPackageTask.new(SPEC) do |pkg|
|
36
|
+
pkg.need_zip = false
|
37
|
+
pkg.need_tar = false
|
38
|
+
end
|
39
|
+
EOF
|
40
|
+
end
|
41
|
+
|
42
|
+
def template_rake_extension(extension_name, gem_spec = nil)
|
43
|
+
<<-EOF
|
44
|
+
require 'rake/extensiontask'
|
45
|
+
Rake::ExtensionTask.new("#{extension_name}"#{', SPEC' if gem_spec})
|
46
|
+
EOF
|
47
|
+
end
|
48
|
+
|
49
|
+
def template_rake_extension_with_platform(extension_name, platform)
|
50
|
+
<<-EOF
|
51
|
+
require 'rake/extensiontask'
|
52
|
+
Rake::ExtensionTask.new("#{extension_name}", SPEC) do |ext|
|
53
|
+
ext.platform = "#{platform}"
|
54
|
+
end
|
55
|
+
EOF
|
56
|
+
end
|
57
|
+
|
58
|
+
def template_rake_extension_cross_compile(extension_name, gem_spec = nil)
|
59
|
+
<<-EOF
|
60
|
+
require 'rake/extensiontask'
|
61
|
+
Rake::ExtensionTask.new("#{extension_name}"#{', SPEC' if gem_spec}) do |ext|
|
62
|
+
ext.cross_compile = true
|
63
|
+
end
|
64
|
+
EOF
|
65
|
+
end
|
66
|
+
|
67
|
+
def template_extconf(extension_name)
|
68
|
+
<<-EOF
|
69
|
+
require 'mkmf'
|
70
|
+
create_makefile("#{extension_name}")
|
71
|
+
EOF
|
72
|
+
end
|
73
|
+
|
74
|
+
def template_source_c(extension_name)
|
75
|
+
<<-EOF
|
76
|
+
#include "source.h"
|
77
|
+
void Init_#{extension_name}()
|
78
|
+
{
|
79
|
+
printf("source.c of extension #{extension_name}\\n");
|
80
|
+
}
|
81
|
+
EOF
|
82
|
+
end
|
83
|
+
|
84
|
+
def template_source_h
|
85
|
+
<<-EOF
|
86
|
+
#include "ruby.h"
|
87
|
+
EOF
|
88
|
+
end
|
@@ -0,0 +1,77 @@
|
|
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_cross_compile_extension_task_for(extension_name)
|
44
|
+
# create folder structure
|
45
|
+
FileUtils.mkdir_p "ext/#{extension_name}"
|
46
|
+
|
47
|
+
return if File.exist?("tasks/#{extension_name}.rake")
|
48
|
+
|
49
|
+
# create specific extension rakefile
|
50
|
+
# Building a gem?
|
51
|
+
if File.exist?("tasks/gem.rake") then
|
52
|
+
File.open("tasks/gem.rake", 'a+') do |ext_in_gem|
|
53
|
+
ext_in_gem.puts template_rake_extension_cross_compile(extension_name, true)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
File.open("tasks/#{extension_name}.rake", 'w') do |ext_rake|
|
57
|
+
ext_rake.puts template_rake_extension_cross_compile(extension_name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def generate_source_code_for(extension_name)
|
63
|
+
# source C file
|
64
|
+
File.open("ext/#{extension_name}/source.c", 'w') do |c|
|
65
|
+
c.puts template_source_c(extension_name)
|
66
|
+
end
|
67
|
+
|
68
|
+
# header H file
|
69
|
+
File.open("ext/#{extension_name}/source.h", 'w') do |h|
|
70
|
+
h.puts template_source_h
|
71
|
+
end
|
72
|
+
|
73
|
+
# extconf.rb file
|
74
|
+
File.open("ext/#{extension_name}/extconf.rb", 'w') do |ext|
|
75
|
+
ext.puts template_extconf(extension_name)
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,270 @@
|
|
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
|
+
autoload :GemPackageTask, 'rake/gempackagetask'
|
13
|
+
autoload :YAML, 'yaml'
|
14
|
+
|
15
|
+
class ExtensionTask < TaskLib
|
16
|
+
attr_accessor :name
|
17
|
+
attr_accessor :gem_spec
|
18
|
+
attr_accessor :config_script
|
19
|
+
attr_accessor :tmp_dir
|
20
|
+
attr_accessor :ext_dir
|
21
|
+
attr_accessor :lib_dir
|
22
|
+
attr_accessor :platform
|
23
|
+
attr_accessor :config_options
|
24
|
+
attr_accessor :source_pattern
|
25
|
+
attr_accessor :cross_compile
|
26
|
+
attr_accessor :cross_platform
|
27
|
+
attr_accessor :cross_config_options
|
28
|
+
|
29
|
+
def initialize(name = nil, gem_spec = nil)
|
30
|
+
init(name, gem_spec)
|
31
|
+
yield self if block_given?
|
32
|
+
define
|
33
|
+
end
|
34
|
+
|
35
|
+
def init(name = nil, gem_spec = nil)
|
36
|
+
@name = name
|
37
|
+
@gem_spec = gem_spec
|
38
|
+
@config_script = 'extconf.rb'
|
39
|
+
@tmp_dir = 'tmp'
|
40
|
+
@ext_dir = 'ext'
|
41
|
+
@lib_dir = 'lib'
|
42
|
+
@source_pattern = "*.c"
|
43
|
+
@config_options = []
|
44
|
+
@cross_compile = false
|
45
|
+
@cross_config_options = []
|
46
|
+
end
|
47
|
+
|
48
|
+
def platform
|
49
|
+
@platform ||= RUBY_PLATFORM
|
50
|
+
end
|
51
|
+
|
52
|
+
def cross_platform
|
53
|
+
@cross_platform ||= 'i386-mingw32'
|
54
|
+
end
|
55
|
+
|
56
|
+
def define
|
57
|
+
fail "Extension name must be provided." if @name.nil?
|
58
|
+
|
59
|
+
define_compile_tasks
|
60
|
+
|
61
|
+
# only gems with 'ruby' platforms are allowed to define native tasks
|
62
|
+
define_native_tasks if @gem_spec && @gem_spec.platform == 'ruby'
|
63
|
+
|
64
|
+
# only define cross platform functionality when enabled
|
65
|
+
# FIXME: there is no value for having this on Windows or JRuby
|
66
|
+
define_cross_platform_tasks if @cross_compile
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
def define_compile_tasks(for_platform = nil)
|
71
|
+
# platform usage
|
72
|
+
platf = for_platform || platform
|
73
|
+
|
74
|
+
# tmp_path
|
75
|
+
tmp_path = "#{@tmp_dir}/#{platf}/#{@name}"
|
76
|
+
|
77
|
+
# cleanup and clobbering
|
78
|
+
CLEAN.include(tmp_path)
|
79
|
+
CLOBBER.include("#{@lib_dir}/#{binary}")
|
80
|
+
CLOBBER.include("#{@tmp_dir}")
|
81
|
+
|
82
|
+
# directories we need
|
83
|
+
directory tmp_path
|
84
|
+
directory lib_dir
|
85
|
+
|
86
|
+
# copy binary from temporary location to final lib
|
87
|
+
# tmp/extension_name/extension_name.{so,bundle} => lib/
|
88
|
+
task "copy:#{@name}:#{platf}" => [lib_dir, "#{tmp_path}/#{binary}"] do
|
89
|
+
cp "#{tmp_path}/#{binary}", "#{@lib_dir}/#{binary}"
|
90
|
+
end
|
91
|
+
|
92
|
+
# binary in temporary folder depends on makefile and source files
|
93
|
+
# tmp/extension_name/extension_name.{so,bundle}
|
94
|
+
file "#{tmp_path}/#{binary}" => ["#{tmp_path}/Makefile"] + source_files do
|
95
|
+
chdir tmp_path do
|
96
|
+
sh make
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# makefile depends of tmp_dir and config_script
|
101
|
+
# tmp/extension_name/Makefile
|
102
|
+
file "#{tmp_path}/Makefile" => [tmp_path, extconf] do |t|
|
103
|
+
options = @config_options.dup
|
104
|
+
|
105
|
+
# rbconfig.rb will be present if we are cross compiling
|
106
|
+
if t.prerequisites.include?("#{tmp_path}/rbconfig.rb") then
|
107
|
+
options.push(*@cross_config_options)
|
108
|
+
end
|
109
|
+
|
110
|
+
parent = Dir.pwd
|
111
|
+
chdir tmp_path do
|
112
|
+
# FIXME: Rake is broken for multiple arguments system() calls.
|
113
|
+
# Add current directory to the search path of Ruby
|
114
|
+
# Also, include additional parameters supplied.
|
115
|
+
ruby ['-I.', File.join(parent, extconf), *options].join(' ')
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# compile tasks
|
120
|
+
unless Rake::Task.task_defined?('compile') then
|
121
|
+
desc "Compile all the extensions"
|
122
|
+
task "compile"
|
123
|
+
end
|
124
|
+
|
125
|
+
# compile:name
|
126
|
+
unless Rake::Task.task_defined?("compile:#{@name}") then
|
127
|
+
desc "Compile #{@name}"
|
128
|
+
task "compile:#{@name}"
|
129
|
+
end
|
130
|
+
|
131
|
+
# Allow segmented compilation by platfrom (open door for 'cross compile')
|
132
|
+
task "compile:#{@name}:#{platf}" => ["copy:#{@name}:#{platf}"]
|
133
|
+
task "compile:#{platf}" => ["compile:#{@name}:#{platf}"]
|
134
|
+
|
135
|
+
# Only add this extension to the compile chain if current
|
136
|
+
# platform matches the indicated one.
|
137
|
+
if platf == RUBY_PLATFORM then
|
138
|
+
# ensure file is always copied
|
139
|
+
file "#{@lib_dir}/#{binary}" => ["copy:#{name}:#{platf}"]
|
140
|
+
|
141
|
+
task "compile:#{@name}" => ["compile:#{@name}:#{platf}"]
|
142
|
+
task "compile" => ["compile:#{platf}"]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def define_native_tasks(for_platform = nil)
|
147
|
+
platf = for_platform || platform
|
148
|
+
|
149
|
+
# tmp_path
|
150
|
+
tmp_path = "#{@tmp_dir}/#{platf}/#{@name}"
|
151
|
+
|
152
|
+
# create 'native:gem_name' and chain it to 'native' task
|
153
|
+
spec = @gem_spec.dup
|
154
|
+
|
155
|
+
unless Rake::Task.task_defined?("native:#{@gem_spec.name}:#{platf}")
|
156
|
+
task "native:#{@gem_spec.name}:#{platf}" do |t|
|
157
|
+
# adjust to specified platform
|
158
|
+
spec.platform = platf
|
159
|
+
|
160
|
+
# clear the extensions defined in the specs
|
161
|
+
spec.extensions.clear
|
162
|
+
|
163
|
+
# add the binaries that this task depends on
|
164
|
+
# ensure the files get properly copied to lib_dir
|
165
|
+
ext_files = t.prerequisites.map { |ext| "#{@lib_dir}/#{File.basename(ext)}" }
|
166
|
+
ext_files.each do |ext|
|
167
|
+
unless Rake::Task.task_defined?("#{@lib_dir}/#{File.basename(ext)}") then
|
168
|
+
# strip out path and .so/.bundle
|
169
|
+
file "#{@lib_dir}/#{File.basename(ext)}" => ["copy:#{File.basename(ext).ext('')}:#{platf}"]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# include the files in the gem specification
|
174
|
+
spec.files += ext_files
|
175
|
+
|
176
|
+
# Generate a package for this gem
|
177
|
+
gem_package = Rake::GemPackageTask.new(spec) do |pkg|
|
178
|
+
pkg.need_zip = false
|
179
|
+
pkg.need_tar = false
|
180
|
+
end
|
181
|
+
|
182
|
+
# ensure the binaries are copied
|
183
|
+
task "#{gem_package.package_dir}/#{gem_package.gem_file}" => ["copy:#{@name}:#{platf}"]
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# add binaries to the dependency chain
|
188
|
+
task "native:#{@gem_spec.name}:#{platf}" => ["#{tmp_path}/#{binary}"]
|
189
|
+
|
190
|
+
# Allow segmented packaging by platfrom (open door for 'cross compile')
|
191
|
+
task "native:#{platf}" => ["native:#{@gem_spec.name}:#{platf}"]
|
192
|
+
|
193
|
+
# Only add this extension to the compile chain if current
|
194
|
+
# platform matches the indicated one.
|
195
|
+
if platf == RUBY_PLATFORM then
|
196
|
+
task "native:#{@gem_spec.name}" => ["native:#{@gem_spec.name}:#{platf}"]
|
197
|
+
task "native" => ["native:#{platf}"]
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def define_cross_platform_tasks
|
202
|
+
config_path = File.expand_path("~/.rake-compiler/config.yml")
|
203
|
+
major_ver = (ENV['RUBY_CC_VERSION'] || RUBY_VERSION).match(/(\d+.\d+)/)[1]
|
204
|
+
|
205
|
+
# warn the user about the need of configuration to use cross compilation.
|
206
|
+
unless File.exist?(config_path)
|
207
|
+
warn "rake-compiler must be configured first to enable cross-compilation"
|
208
|
+
return
|
209
|
+
end
|
210
|
+
|
211
|
+
config_file = YAML.load_file(config_path)
|
212
|
+
|
213
|
+
# tmp_path
|
214
|
+
tmp_path = "#{@tmp_dir}/#{cross_platform}/#{@name}"
|
215
|
+
|
216
|
+
unless rbconfig_file = config_file["rbconfig-#{major_ver}"] then
|
217
|
+
fail "no configuration section for this version of Ruby (rbconfig-#{major_ver})"
|
218
|
+
end
|
219
|
+
|
220
|
+
# define compilation tasks for cross platfrom!
|
221
|
+
define_compile_tasks(cross_platform)
|
222
|
+
|
223
|
+
# chain rbconfig.rb to Makefile generation
|
224
|
+
file "#{tmp_path}/Makefile" => ["#{tmp_path}/rbconfig.rb"]
|
225
|
+
|
226
|
+
# copy the file from the cross-ruby location
|
227
|
+
file "#{tmp_path}/rbconfig.rb" => [rbconfig_file] do |t|
|
228
|
+
cp t.prerequisites.first, t.name
|
229
|
+
end
|
230
|
+
|
231
|
+
# now define native tasks for cross compiled files
|
232
|
+
define_native_tasks(cross_platform) if @gem_spec && @gem_spec.platform == 'ruby'
|
233
|
+
|
234
|
+
# create cross task
|
235
|
+
task 'cross' do
|
236
|
+
# clear compile dependencies
|
237
|
+
Rake::Task['compile'].prerequisites.clear
|
238
|
+
|
239
|
+
# chain the cross platform ones
|
240
|
+
task 'compile' => ["compile:#{cross_platform}"]
|
241
|
+
|
242
|
+
# clear lib/binary dependencies and trigger cross platform ones
|
243
|
+
Rake::Task["#{@lib_dir}/#{binary}"].prerequisites.clear
|
244
|
+
file "#{@lib_dir}/#{binary}" => ["copy:#{@name}:#{cross_platform}"]
|
245
|
+
|
246
|
+
# if everything for native task is in place
|
247
|
+
if @gem_spec && @gem_spec.platform == 'ruby' then
|
248
|
+
Rake::Task['native'].prerequisites.clear
|
249
|
+
task 'native' => ["native:#{cross_platform}"]
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def extconf
|
255
|
+
"#{@ext_dir}/#{@name}/#{@config_script}"
|
256
|
+
end
|
257
|
+
|
258
|
+
def make
|
259
|
+
RUBY_PLATFORM =~ /mswin/ ? 'nmake' : 'make'
|
260
|
+
end
|
261
|
+
|
262
|
+
def binary
|
263
|
+
"#{@name}.#{RbConfig::CONFIG['DLEXT']}"
|
264
|
+
end
|
265
|
+
|
266
|
+
def source_files
|
267
|
+
@source_files ||= FileList["#{@ext_dir}/#{@name}/#{@source_pattern}"]
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|