rake-compiler-sgonyea 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/History.txt +254 -0
  2. data/Isolate +6 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.rdoc +393 -0
  5. data/Rakefile +21 -0
  6. data/bin/rake-compiler +24 -0
  7. data/cucumber.yml +4 -0
  8. data/features/compile.feature +79 -0
  9. data/features/cross-compile.feature +22 -0
  10. data/features/cross-package-multi.feature +15 -0
  11. data/features/cross-package.feature +14 -0
  12. data/features/java-compile.feature +22 -0
  13. data/features/java-no-native-compile.feature +33 -0
  14. data/features/java-package.feature +24 -0
  15. data/features/package.feature +40 -0
  16. data/features/step_definitions/compilation.rb +70 -0
  17. data/features/step_definitions/cross_compilation.rb +25 -0
  18. data/features/step_definitions/execution.rb +52 -0
  19. data/features/step_definitions/folders.rb +32 -0
  20. data/features/step_definitions/gem.rb +46 -0
  21. data/features/step_definitions/java_compilation.rb +7 -0
  22. data/features/support/env.rb +6 -0
  23. data/features/support/file_template_helpers.rb +137 -0
  24. data/features/support/generator_helpers.rb +123 -0
  25. data/features/support/platform_extension_helpers.rb +27 -0
  26. data/lib/rake/baseextensiontask.rb +90 -0
  27. data/lib/rake/extensioncompiler.rb +53 -0
  28. data/lib/rake/extensiontask.rb +431 -0
  29. data/lib/rake/javaextensiontask.rb +226 -0
  30. data/spec/lib/rake/extensiontask_spec.rb +481 -0
  31. data/spec/lib/rake/javaextensiontask_spec.rb +182 -0
  32. data/spec/spec.opts +3 -0
  33. data/spec/spec_helper.rb +15 -0
  34. data/spec/support/capture_output_helper.rb +22 -0
  35. data/tasks/bin/cross-ruby.rake +196 -0
  36. data/tasks/bootstrap.rake +11 -0
  37. data/tasks/common.rake +10 -0
  38. data/tasks/cucumber.rake +23 -0
  39. data/tasks/gem.rake +53 -0
  40. data/tasks/news.rake +39 -0
  41. data/tasks/release.rake +26 -0
  42. data/tasks/rspec.rake +9 -0
  43. metadata +145 -0
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Rake
4
+
5
+ #
6
+ # HACK: Lousy API design, sue me. At least works ;-)
7
+ #
8
+ # Define a series of helpers to aid in search and usage of MinGW (GCC) Compiler
9
+ # by gem developer/creators.
10
+ #
11
+ module ExtensionCompiler
12
+ # return the host portion from the installed MinGW
13
+ def self.mingw_host
14
+ return @mingw_host if @mingw_host
15
+
16
+ # the mingw_gcc_executable is helpful here
17
+ if target = mingw_gcc_executable then
18
+ # we only care for the filename
19
+ target = File.basename(target)
20
+
21
+ # now strip the extension (if present)
22
+ target.sub!(File.extname(target), '')
23
+
24
+ # get rid of '-gcc' portion too ;-)
25
+ target.sub!('-gcc', '')
26
+ end
27
+
28
+ raise "No MinGW tools or unknown setup platform?" unless target
29
+
30
+ @mingw_host = target
31
+ end
32
+
33
+ # return the first compiler found that includes both mingw and gcc conditions
34
+ # (this assumes you have one installed)
35
+ def self.mingw_gcc_executable
36
+ return @mingw_gcc_executable if @mingw_gcc_executable
37
+
38
+ # grab the paths defined in the environment
39
+ paths = ENV['PATH'].split(File::PATH_SEPARATOR)
40
+
41
+ # the pattern to look into (captures *nix and windows executables)
42
+ pattern = "i?86*mingw*gcc{,.*}"
43
+
44
+ @mingw_gcc_executable = paths.find do |path|
45
+ # cleanup paths before globbing
46
+ gcc = Dir.glob("#{File.expand_path(path)}/#{pattern}").first
47
+ break gcc if gcc
48
+ end
49
+
50
+ @mingw_gcc_executable
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,431 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rake/baseextensiontask'
4
+ require "rubygems/package_task"
5
+
6
+ # Define a series of tasks to aid in the compilation of C extensions for
7
+ # gem developer/creators.
8
+
9
+ module Rake
10
+ class ExtensionTask < BaseExtensionTask
11
+
12
+ attr_accessor :config_script
13
+ attr_accessor :cross_compile
14
+ attr_accessor :cross_platform
15
+ attr_accessor :cross_config_options
16
+ attr_accessor :no_native
17
+ attr_accessor :config_includes
18
+
19
+ def init(name = nil, gem_spec = nil)
20
+ super
21
+ @config_script = 'extconf.rb'
22
+ @source_pattern = "*.c"
23
+ @compiled_pattern = "*.{o,obj,so,bundle,dSYM}"
24
+ @cross_compile = false
25
+ @cross_config_options = []
26
+ @cross_compiling = nil
27
+ @no_native = false
28
+ @config_includes = []
29
+ end
30
+
31
+ def cross_platform
32
+ @cross_platform ||= 'i386-mingw32'
33
+ end
34
+
35
+ def cross_compiling(&block)
36
+ @cross_compiling = block if block_given?
37
+ end
38
+
39
+ def binary(platform = nil)
40
+ if platform == "java"
41
+ "#{name}.#{RbConfig::MAKEFILE_CONFIG['DLEXT']}"
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ def define
48
+ if (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ironruby')
49
+ warn_once <<-EOF
50
+ WARNING: You're attempting to (cross-)compile C extensions from a platform
51
+ (#{RUBY_ENGINE}) that does not support native extensions or mkmf.rb.
52
+ Rerun `rake` under MRI Ruby 1.8.x/1.9.x to cross/native compile.
53
+ EOF
54
+ return
55
+ end
56
+
57
+ super
58
+
59
+ unless compiled_files.empty?
60
+ warn "WARNING: rake-compiler found compiled files in '#{@ext_dir}' directory. Please remove them."
61
+ end
62
+
63
+ # only gems with 'ruby' platforms are allowed to define native tasks
64
+ define_native_tasks if !@no_native && (@gem_spec && @gem_spec.platform == 'ruby')
65
+
66
+ # only define cross platform functionality when enabled
67
+ return unless @cross_compile
68
+
69
+ if cross_platform.is_a?(Array) then
70
+ cross_platform.each { |platf| define_cross_platform_tasks(platf) }
71
+ else
72
+ define_cross_platform_tasks(cross_platform)
73
+ end
74
+ end
75
+
76
+ private
77
+ def define_compile_tasks(for_platform = nil, ruby_ver = RUBY_VERSION)
78
+ # platform usage
79
+ platf = for_platform || platform
80
+
81
+ # lib_path
82
+ lib_path = lib_dir
83
+
84
+ # tmp_path
85
+ tmp_path = "#{@tmp_dir}/#{platf}/#{@name}/#{ruby_ver}"
86
+
87
+ # cleanup and clobbering
88
+ CLEAN.include(tmp_path)
89
+ CLOBBER.include("#{lib_path}/#{binary(platf)}")
90
+ CLOBBER.include("#{@tmp_dir}")
91
+
92
+ # directories we need
93
+ directory tmp_path
94
+ directory lib_dir
95
+
96
+ # copy binary from temporary location to final lib
97
+ # tmp/extension_name/extension_name.{so,bundle} => lib/
98
+ task "copy:#{@name}:#{platf}:#{ruby_ver}" => [lib_path, "#{tmp_path}/#{binary(platf)}"] do
99
+ install "#{tmp_path}/#{binary(platf)}", "#{lib_path}/#{binary(platf)}"
100
+ end
101
+
102
+ # binary in temporary folder depends on makefile and source files
103
+ # tmp/extension_name/extension_name.{so,bundle}
104
+ file "#{tmp_path}/#{binary(platf)}" => ["#{tmp_path}/Makefile"] + source_files do
105
+ jruby_compile_msg = <<-EOF
106
+ Compiling a native C extension on JRuby. This is discouraged and a
107
+ Java extension should be preferred.
108
+ EOF
109
+ warn_once(jruby_compile_msg) if defined?(JRUBY_VERSION)
110
+
111
+ chdir tmp_path do
112
+ sh make
113
+ end
114
+ end
115
+
116
+ # makefile depends of tmp_dir and config_script
117
+ # tmp/extension_name/Makefile
118
+ file "#{tmp_path}/Makefile" => [tmp_path, extconf] do |t|
119
+ options = @config_options.dup
120
+
121
+ # include current directory
122
+ include_dirs = ['.'].concat(@config_includes).uniq.join(File::PATH_SEPARATOR)
123
+ cmd = [Gem.ruby, "-I#{include_dirs}"]
124
+
125
+ # if fake.rb is present, add to the command line
126
+ if t.prerequisites.include?("#{tmp_path}/fake.rb") then
127
+ cmd << '-rfake'
128
+ end
129
+
130
+ # build a relative path to extconf script
131
+ abs_tmp_path = (Pathname.new(Dir.pwd) + tmp_path).realpath
132
+ abs_extconf = (Pathname.new(Dir.pwd) + extconf).realpath
133
+
134
+ # now add the extconf script
135
+ cmd << abs_extconf.relative_path_from(abs_tmp_path)
136
+
137
+ # rbconfig.rb will be present if we are cross compiling
138
+ if t.prerequisites.include?("#{tmp_path}/rbconfig.rb") then
139
+ options.push(*@cross_config_options)
140
+ end
141
+
142
+ # add options to command
143
+ cmd.push(*options)
144
+
145
+ # add any extra command line options
146
+ unless extra_options.empty?
147
+ cmd.push(*extra_options)
148
+ end
149
+
150
+ chdir tmp_path do
151
+ # FIXME: Rake is broken for multiple arguments system() calls.
152
+ # Add current directory to the search path of Ruby
153
+ sh cmd.join(' ')
154
+ end
155
+ end
156
+
157
+ # compile tasks
158
+ unless Rake::Task.task_defined?('compile') then
159
+ desc "Compile all the extensions"
160
+ task "compile"
161
+ end
162
+
163
+ # compile:name
164
+ unless Rake::Task.task_defined?("compile:#{@name}") then
165
+ desc "Compile #{@name}"
166
+ task "compile:#{@name}"
167
+ end
168
+
169
+ # Allow segmented compilation by platform (open door for 'cross compile')
170
+ task "compile:#{@name}:#{platf}" => ["copy:#{@name}:#{platf}:#{ruby_ver}"]
171
+ task "compile:#{platf}" => ["compile:#{@name}:#{platf}"]
172
+
173
+ # Only add this extension to the compile chain if current
174
+ # platform matches the indicated one.
175
+ if platf == RUBY_PLATFORM then
176
+ # ensure file is always copied
177
+ file "#{lib_path}/#{binary(platf)}" => ["copy:#{name}:#{platf}:#{ruby_ver}"]
178
+
179
+ task "compile:#{@name}" => ["compile:#{@name}:#{platf}"]
180
+ task "compile" => ["compile:#{platf}"]
181
+ end
182
+ end
183
+
184
+ def define_native_tasks(for_platform = nil, ruby_ver = RUBY_VERSION, callback = nil)
185
+ platf = for_platform || platform
186
+
187
+ # tmp_path
188
+ tmp_path = "#{@tmp_dir}/#{platf}/#{@name}/#{ruby_ver}"
189
+
190
+ # lib_path
191
+ lib_path = lib_dir
192
+
193
+ # create 'native:gem_name' and chain it to 'native' task
194
+ unless Rake::Task.task_defined?("native:#{@gem_spec.name}:#{platf}")
195
+ task "native:#{@gem_spec.name}:#{platf}" do |t|
196
+ # FIXME: workaround Gem::Specification limitation around cache_file:
197
+ # http://github.com/rubygems/rubygems/issues/78
198
+ spec = gem_spec.dup
199
+ spec.instance_variable_set(:"@cache_file", nil) if spec.respond_to?(:cache_file)
200
+
201
+ # adjust to specified platform
202
+ spec.platform = Gem::Platform.new(platf)
203
+
204
+ # clear the extensions defined in the specs
205
+ spec.extensions.clear
206
+
207
+ # add the binaries that this task depends on
208
+ ext_files = []
209
+
210
+ # go through native prerequisites and grab the real extension files from there
211
+ t.prerequisites.each do |ext|
212
+ ext_files << ext
213
+ end
214
+
215
+ # include the files in the gem specification
216
+ spec.files += ext_files
217
+
218
+ # expose gem specification for customization
219
+ if callback
220
+ callback.call(spec)
221
+ end
222
+
223
+ # Generate a package for this gem
224
+ Gem::PackageTask.new(spec) do |pkg|
225
+ pkg.need_zip = false
226
+ pkg.need_tar = false
227
+ end
228
+ end
229
+ end
230
+
231
+ # add binaries to the dependency chain
232
+ task "native:#{@gem_spec.name}:#{platf}" => ["#{lib_path}/#{binary(platf)}"]
233
+
234
+ # ensure the extension get copied
235
+ unless Rake::Task.task_defined?("#{lib_path}/#{binary(platf)}") then
236
+ file "#{lib_path}/#{binary(platf)}" => ["copy:#{@name}:#{platf}:#{ruby_ver}"]
237
+ end
238
+
239
+ # Allow segmented packaging by platform (open door for 'cross compile')
240
+ task "native:#{platf}" => ["native:#{@gem_spec.name}:#{platf}"]
241
+
242
+ # Only add this extension to the compile chain if current
243
+ # platform matches the indicated one.
244
+ if platf == RUBY_PLATFORM then
245
+ task "native:#{@gem_spec.name}" => ["native:#{@gem_spec.name}:#{platf}"]
246
+ task "native" => ["native:#{platf}"]
247
+ end
248
+ end
249
+
250
+ def define_cross_platform_tasks(for_platform)
251
+ if ruby_vers = ENV['RUBY_CC_VERSION']
252
+ ruby_vers = ENV['RUBY_CC_VERSION'].split(':')
253
+ else
254
+ ruby_vers = [RUBY_VERSION]
255
+ end
256
+
257
+ multi = (ruby_vers.size > 1) ? true : false
258
+
259
+ ruby_vers.each do |version|
260
+ # save original lib_dir
261
+ orig_lib_dir = @lib_dir
262
+
263
+ # tweak lib directory only when targeting multiple versions
264
+ if multi then
265
+ version =~ /(\d+.\d+)/
266
+ @lib_dir = "#{@lib_dir}/#{$1}"
267
+ end
268
+
269
+ define_cross_platform_tasks_with_version(for_platform, version)
270
+
271
+ # restore lib_dir
272
+ @lib_dir = orig_lib_dir
273
+ end
274
+ end
275
+
276
+ def define_cross_platform_tasks_with_version(for_platform, ruby_ver)
277
+ config_path = File.expand_path("~/.rake-compiler/config.yml")
278
+
279
+ # warn the user about the need of configuration to use cross compilation.
280
+ unless File.exist?(config_path)
281
+ define_dummy_cross_platform_tasks
282
+ return
283
+ end
284
+
285
+ config_file = YAML.load_file(config_path)
286
+
287
+ # tmp_path
288
+ tmp_path = "#{@tmp_dir}/#{for_platform}/#{@name}/#{ruby_ver}"
289
+
290
+ # lib_path
291
+ lib_path = lib_dir
292
+
293
+ unless rbconfig_file = config_file["rbconfig-#{ruby_ver}"] then
294
+ warn "no configuration section for specified version of Ruby (rbconfig-#{ruby_ver})"
295
+ return
296
+ end
297
+
298
+ # mkmf
299
+ mkmf_file = File.expand_path(File.join(File.dirname(rbconfig_file), '..', 'mkmf.rb'))
300
+
301
+ # define compilation tasks for cross platform!
302
+ define_compile_tasks(for_platform, ruby_ver)
303
+
304
+ # chain fake.rb, rbconfig.rb and mkmf.rb to Makefile generation
305
+ file "#{tmp_path}/Makefile" => ["#{tmp_path}/fake.rb",
306
+ "#{tmp_path}/rbconfig.rb",
307
+ "#{tmp_path}/mkmf.rb"]
308
+
309
+ # copy the file from the cross-ruby location
310
+ file "#{tmp_path}/rbconfig.rb" => [rbconfig_file] do |t|
311
+ cp t.prerequisites.first, t.name
312
+ end
313
+
314
+ # copy mkmf from cross-ruby location
315
+ file "#{tmp_path}/mkmf.rb" => [mkmf_file] do |t|
316
+ cp t.prerequisites.first, t.name
317
+ end
318
+
319
+ # genearte fake.rb for different ruby versions
320
+ file "#{tmp_path}/fake.rb" do |t|
321
+ File.open(t.name, 'w') do |f|
322
+ f.write fake_rb(for_platform, ruby_ver)
323
+ end
324
+ end
325
+
326
+ # now define native tasks for cross compiled files
327
+ if @gem_spec && @gem_spec.platform == 'ruby' then
328
+ define_native_tasks(for_platform, ruby_ver, @cross_compiling)
329
+ end
330
+
331
+ # create cross task
332
+ task 'cross' do
333
+ # clear compile dependencies
334
+ Rake::Task['compile'].prerequisites.reject! { |t| !compiles_cross_platform.include?(t) }
335
+
336
+ # chain the cross platform ones
337
+ task 'compile' => ["compile:#{for_platform}"]
338
+
339
+ # clear lib/binary dependencies and trigger cross platform ones
340
+ # check if lib/binary is defined (damn bundle versus so versus dll)
341
+ if Rake::Task.task_defined?("#{lib_path}/#{binary(for_platform)}") then
342
+ Rake::Task["#{lib_path}/#{binary(for_platform)}"].prerequisites.clear
343
+ end
344
+
345
+ # FIXME: targeting multiple platforms copies the file twice
346
+ file "#{lib_path}/#{binary(for_platform)}" => ["copy:#{@name}:#{for_platform}:#{ruby_ver}"]
347
+
348
+ # if everything for native task is in place
349
+ if @gem_spec && @gem_spec.platform == 'ruby' then
350
+ # double check: only cross platform native tasks should be here
351
+ # FIXME: Sooo brittle
352
+ Rake::Task['native'].prerequisites.reject! { |t| !natives_cross_platform.include?(t) }
353
+ task 'native' => ["native:#{for_platform}"]
354
+ end
355
+ end
356
+ end
357
+
358
+ def define_dummy_cross_platform_tasks
359
+ task 'cross' do
360
+ Rake::Task['compile'].clear
361
+ task 'compile' do
362
+ raise "rake-compiler must be configured first to enable cross-compilation"
363
+ end
364
+ end
365
+ end
366
+
367
+ def extconf
368
+ "#{@ext_dir}/#{@config_script}"
369
+ end
370
+
371
+ def make
372
+ unless @make
373
+ @make =
374
+ if RUBY_PLATFORM =~ /mswin/ then
375
+ 'nmake'
376
+ else
377
+ ENV['MAKE'] || %w[gmake make].find { |c|
378
+ system("#{c} -v >> #{dev_null} 2>&1")
379
+ }
380
+ end
381
+ end
382
+
383
+ unless @make
384
+ raise "Couldn't find a suitable `make` tool. Use `MAKE` env to set an alternative."
385
+ end
386
+
387
+ @make
388
+ end
389
+
390
+ def dev_null
391
+ windows? ? 'NUL' : '/dev/null'
392
+ end
393
+
394
+ def compiled_files
395
+ FileList["#{@ext_dir}/#{@compiled_pattern}"]
396
+ end
397
+
398
+ def compiles_cross_platform
399
+ [*@cross_platform].map { |p| "compile:#{p}" }
400
+ end
401
+
402
+ def natives_cross_platform
403
+ [*@cross_platform].map { |p| "native:#{p}" }
404
+ end
405
+
406
+ def fake_rb(platform, version)
407
+ <<-FAKE_RB
408
+ class Object
409
+ remove_const :RUBY_PLATFORM
410
+ remove_const :RUBY_VERSION
411
+ remove_const :RUBY_DESCRIPTION if defined?(RUBY_DESCRIPTION)
412
+ RUBY_PLATFORM = "#{platform}"
413
+ RUBY_VERSION = "#{version}"
414
+ RUBY_DESCRIPTION = "ruby \#{RUBY_VERSION} (\#{RUBY_RELEASE_DATE}) [\#{RUBY_PLATFORM}]"
415
+ end
416
+ if RUBY_PLATFORM =~ /mswin|bccwin|mingw/
417
+ class File
418
+ remove_const :ALT_SEPARATOR
419
+ ALT_SEPARATOR = "\\\\"
420
+ end
421
+ end
422
+
423
+ posthook = proc do
424
+ $ruby = "#{Gem.ruby}"
425
+ untrace_var(:$ruby, posthook)
426
+ end
427
+ trace_var(:$ruby, posthook)
428
+ FAKE_RB
429
+ end
430
+ end
431
+ end