rake-compiler-sgonyea 0.8.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.
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