rake-compiler 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ Given %r{^I've installed the Java Development Kit$} do
2
+ pending('Cannot locate suitable Java compiler (the Java Development Kit) in the PATH.') unless search_path(%w(javac javac.exe))
3
+ end
4
+
5
+ Given %r{^I've installed JRuby$} do
6
+ pending('Cannot locate a JRuby installation in the PATH.') unless search_path(%w(jruby jruby.exe jruby.bat))
7
+ end
@@ -1,4 +1,3 @@
1
- require 'rubygems'
2
1
  require 'cucumber'
3
2
  require 'spec'
4
3
  require 'fileutils'
@@ -75,6 +75,15 @@ end
75
75
  EOF
76
76
  end
77
77
 
78
+ def template_rake_extension_java_compile(extension_name, gem_spec = nil)
79
+ <<-EOF
80
+ require 'rake/javaextensiontask'
81
+ Rake::JavaExtensionTask.new("#{extension_name}"#{', SPEC' if gem_spec}) do |ext|
82
+ # nothing
83
+ end
84
+ EOF
85
+ end
86
+
78
87
  def template_extconf(extension_name)
79
88
  <<-EOF
80
89
  require 'mkmf'
@@ -97,6 +106,33 @@ EOF
97
106
  #include "ruby.h"
98
107
  EOF
99
108
  end
109
+
110
+ def template_source_java(extension_name)
111
+ <<-EOF
112
+ import org.jruby.Ruby;
113
+ import org.jruby.runtime.load.BasicLibraryService;
114
+
115
+ public class #{camelize(extension_name)}Service implements BasicLibraryService {
116
+ public boolean basicLoad(final Ruby runtime) throws java.io.IOException {
117
+ HelloWorldPrinter hwp = new HelloWorldPrinter();
118
+ hwp.tellTheWorld();
119
+ return true;
120
+ }
121
+
122
+ private class HelloWorldPrinter {
123
+ void tellTheWorld() throws java.io.IOException {
124
+ System.out.println("#{camelize(extension_name)}Service.java of extension #{extension_name}\\n");
125
+ }
126
+ }
127
+ }
128
+
129
+ EOF
130
+ end
131
+
132
+ def camelize(str)
133
+ str.gsub(/(^|_)(.)/) { $2.upcase }
134
+ end
135
+
100
136
  end
101
137
 
102
138
  World(FileTemplateHelpers)
@@ -4,7 +4,7 @@ module GeneratorHelpers
4
4
  FileUtils.mkdir_p "lib"
5
5
  FileUtils.mkdir_p "tasks"
6
6
  FileUtils.mkdir_p "tmp"
7
-
7
+
8
8
  # create Rakefile loader
9
9
  File.open("Rakefile", 'w') do |rakefile|
10
10
  rakefile.puts template_rakefile.strip
@@ -60,6 +60,25 @@ module GeneratorHelpers
60
60
  end
61
61
  end
62
62
 
63
+ def generate_java_compile_extension_task_for(extension_name)
64
+ # create folder structure
65
+ FileUtils.mkdir_p "ext/#{extension_name}"
66
+
67
+ return if File.exist?("tasks/#{extension_name}.rake")
68
+
69
+ # create specific extension rakefile
70
+ # Building a gem?
71
+ if File.exist?("tasks/gem.rake") then
72
+ File.open("tasks/gem.rake", 'a+') do |ext_in_gem|
73
+ ext_in_gem.puts template_rake_extension_java_compile(extension_name, true)
74
+ end
75
+ else
76
+ File.open("tasks/#{extension_name}.rake", 'w') do |ext_rake|
77
+ ext_rake.puts template_rake_extension_java_compile(extension_name)
78
+ end
79
+ end
80
+ end
81
+
63
82
  def generate_multi_cross_compile_extension_task_for(extension_name)
64
83
  # create folder structure
65
84
  FileUtils.mkdir_p "ext/#{extension_name}"
@@ -91,6 +110,14 @@ module GeneratorHelpers
91
110
  ext.puts template_extconf(extension_name)
92
111
  end
93
112
  end
113
+
114
+ def generate_java_source_code_for(extension_name)
115
+ # source .java file
116
+ File.open("ext/#{extension_name}/#{camelize(extension_name)}Service.java", 'w') do |c|
117
+ c.puts template_source_java(extension_name)
118
+ end
119
+ end
120
+
94
121
  end
95
122
 
96
123
  World(GeneratorHelpers)
@@ -5,10 +5,23 @@ module PlatformExtensionHelpers
5
5
  'bundle'
6
6
  when /mingw|mswin|linux/
7
7
  'so'
8
+ when /java/
9
+ 'jar'
8
10
  else
9
11
  RbConfig::CONFIG['DLEXT']
10
12
  end
11
13
  end
14
+
15
+ def search_path(binaries)
16
+ paths = ENV['PATH'].split(File::PATH_SEPARATOR)
17
+ binary = binaries.find do |bin_file|
18
+ paths.find do |path|
19
+ bin = File.join(path, bin_file)
20
+ File.exist?(bin) && File.executable?(bin)
21
+ end
22
+ end
23
+ binary
24
+ end
12
25
  end
13
26
 
14
27
  World(PlatformExtensionHelpers)
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rake'
4
+ require 'rake/clean'
5
+ require 'rake/tasklib'
6
+ require 'rbconfig'
7
+ require 'yaml'
8
+ require 'pathname'
9
+
10
+ module Rake
11
+ autoload :GemPackageTask, 'rake/gempackagetask'
12
+
13
+ class BaseExtensionTask < TaskLib
14
+
15
+ attr_accessor :name
16
+ attr_accessor :gem_spec
17
+ attr_accessor :tmp_dir
18
+ attr_accessor :ext_dir
19
+ attr_accessor :lib_dir
20
+ attr_accessor :platform
21
+ attr_accessor :config_options
22
+ attr_accessor :source_pattern
23
+
24
+ def platform
25
+ @platform ||= RUBY_PLATFORM
26
+ end
27
+
28
+ def initialize(name = nil, gem_spec = nil)
29
+ init(name, gem_spec)
30
+ yield self if block_given?
31
+ define
32
+ end
33
+
34
+ def init(name = nil, gem_spec = nil)
35
+ @name = name
36
+ @gem_spec = gem_spec
37
+ @tmp_dir = 'tmp'
38
+ @ext_dir = "ext/#{@name}"
39
+ @lib_dir = 'lib'
40
+ @config_options = []
41
+ end
42
+
43
+ def define
44
+ fail "Extension name must be provided." if @name.nil?
45
+
46
+ define_compile_tasks
47
+ end
48
+
49
+ private
50
+
51
+ def define_compile_tasks
52
+ raise NotImplementedError
53
+ end
54
+
55
+ def binary(platform = nil)
56
+ ext = case platform
57
+ when /darwin/
58
+ 'bundle'
59
+ when /mingw|mswin|linux/
60
+ 'so'
61
+ when /java/
62
+ 'jar'
63
+ else
64
+ RbConfig::CONFIG['DLEXT']
65
+ end
66
+ "#{@name}.#{ext}"
67
+ end
68
+
69
+ def source_files
70
+ @source_files ||= FileList["#{@ext_dir}/#{@source_pattern}"]
71
+ end
72
+
73
+ def warn_once(message)
74
+ @@already_warned ||= false
75
+ return if @@already_warned
76
+ @@already_warned = true
77
+ warn message
78
+ end
79
+
80
+ def windows?
81
+ Rake.application.windows?
82
+ end
83
+ end
84
+ end
@@ -50,4 +50,4 @@ module Rake
50
50
  @mingw_gcc_executable
51
51
  end
52
52
  end
53
- end
53
+ end
@@ -1,65 +1,51 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'rake/baseextensiontask'
4
+
3
5
  # Define a series of tasks to aid in the compilation of C extensions for
4
6
  # gem developer/creators.
5
7
 
6
- require 'rake'
7
- require 'rake/clean'
8
- require 'rake/tasklib'
9
- require 'rbconfig'
10
- require 'yaml'
11
-
12
8
  module Rake
13
- autoload :GemPackageTask, 'rake/gempackagetask'
9
+ class ExtensionTask < BaseExtensionTask
14
10
 
15
- class ExtensionTask < TaskLib
16
- attr_accessor :name
17
- attr_accessor :gem_spec
18
11
  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
12
  attr_accessor :cross_compile
26
13
  attr_accessor :cross_platform
27
14
  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
15
+ attr_accessor :no_native
34
16
 
35
17
  def init(name = nil, gem_spec = nil)
36
- @name = name
37
- @gem_spec = gem_spec
18
+ super
38
19
  @config_script = 'extconf.rb'
39
- @tmp_dir = 'tmp'
40
- @ext_dir = "ext/#{@name}"
41
- @lib_dir = 'lib'
42
20
  @source_pattern = "*.c"
43
- @config_options = []
44
21
  @cross_compile = false
45
22
  @cross_config_options = []
46
- end
47
-
48
- def platform
49
- @platform ||= RUBY_PLATFORM
23
+ @cross_compiling = nil
24
+ @no_native = false
50
25
  end
51
26
 
52
27
  def cross_platform
53
28
  @cross_platform ||= 'i386-mingw32'
54
29
  end
55
30
 
31
+ def cross_compiling(&block)
32
+ @cross_compiling = block if block_given?
33
+ end
34
+
56
35
  def define
57
- fail "Extension name must be provided." if @name.nil?
36
+ if RUBY_PLATFORM == 'java' || (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ironruby')
37
+ warn_once <<-EOF
38
+ WARNING: You're attempting to (cross-)compile C extensions from a platform
39
+ (#{RUBY_ENGINE}) that does not support native extensions or mkmf.rb.
40
+ Rerun `rake` under MRI Ruby 1.8.x/1.9.x to cross/native compile.
41
+ EOF
42
+ return
43
+ end
58
44
 
59
- define_compile_tasks
45
+ super
60
46
 
61
47
  # only gems with 'ruby' platforms are allowed to define native tasks
62
- define_native_tasks if @gem_spec && @gem_spec.platform == 'ruby'
48
+ define_native_tasks if !@no_native && (@gem_spec && @gem_spec.platform == 'ruby')
63
49
 
64
50
  # only define cross platform functionality when enabled
65
51
  return unless @cross_compile
@@ -118,8 +104,12 @@ module Rake
118
104
  cmd << '-rfake'
119
105
  end
120
106
 
107
+ # build a relative path to extconf script
108
+ abs_tmp_path = Pathname.new(Dir.pwd) + tmp_path
109
+ abs_extconf = Pathname.new(Dir.pwd) + extconf
110
+
121
111
  # now add the extconf script
122
- cmd << File.join(Dir.pwd, extconf)
112
+ cmd << abs_extconf.relative_path_from(abs_tmp_path)
123
113
 
124
114
  # rbconfig.rb will be present if we are cross compiling
125
115
  if t.prerequisites.include?("#{tmp_path}/rbconfig.rb") then
@@ -164,7 +154,7 @@ module Rake
164
154
  end
165
155
  end
166
156
 
167
- def define_native_tasks(for_platform = nil, ruby_ver = RUBY_VERSION)
157
+ def define_native_tasks(for_platform = nil, ruby_ver = RUBY_VERSION, callback = nil)
168
158
  platf = for_platform || platform
169
159
 
170
160
  # tmp_path
@@ -178,7 +168,7 @@ module Rake
178
168
  task "native:#{@gem_spec.name}:#{platf}" do |t|
179
169
  # FIXME: truly duplicate the Gem::Specification
180
170
  # workaround the lack of #dup for Gem::Specification
181
- spec = Gem::Specification.from_yaml(gem_spec.to_yaml)
171
+ spec = gem_spec.dup
182
172
 
183
173
  # adjust to specified platform
184
174
  spec.platform = Gem::Platform.new(platf)
@@ -197,6 +187,11 @@ module Rake
197
187
  # include the files in the gem specification
198
188
  spec.files += ext_files
199
189
 
190
+ # expose gem specification for customization
191
+ if callback
192
+ callback.call(spec)
193
+ end
194
+
200
195
  # Generate a package for this gem
201
196
  gem_package = Rake::GemPackageTask.new(spec) do |pkg|
202
197
  pkg.need_zip = false
@@ -213,7 +208,7 @@ module Rake
213
208
  file "#{lib_path}/#{binary(platf)}" => ["copy:#{@name}:#{platf}:#{ruby_ver}"]
214
209
  end
215
210
 
216
- # Allow segmented packaging by platfrom (open door for 'cross compile')
211
+ # Allow segmented packaging by platform (open door for 'cross compile')
217
212
  task "native:#{platf}" => ["native:#{@gem_spec.name}:#{platf}"]
218
213
 
219
214
  # Only add this extension to the compile chain if current
@@ -226,7 +221,7 @@ module Rake
226
221
 
227
222
  def define_cross_platform_tasks(for_platform)
228
223
  if ruby_vers = ENV['RUBY_CC_VERSION']
229
- ruby_vers = ENV['RUBY_CC_VERSION'].split(File::PATH_SEPARATOR)
224
+ ruby_vers = ENV['RUBY_CC_VERSION'].split(':')
230
225
  else
231
226
  ruby_vers = [RUBY_VERSION]
232
227
  end
@@ -275,7 +270,7 @@ module Rake
275
270
  # mkmf
276
271
  mkmf_file = File.expand_path(File.join(File.dirname(rbconfig_file), '..', 'mkmf.rb'))
277
272
 
278
- # define compilation tasks for cross platfrom!
273
+ # define compilation tasks for cross platform!
279
274
  define_compile_tasks(for_platform, ruby_ver)
280
275
 
281
276
  # chain fake.rb, rbconfig.rb and mkmf.rb to Makefile generation
@@ -301,7 +296,9 @@ module Rake
301
296
  end
302
297
 
303
298
  # now define native tasks for cross compiled files
304
- define_native_tasks(for_platform, ruby_ver) if @gem_spec && @gem_spec.platform == 'ruby'
299
+ if @gem_spec && @gem_spec.platform == 'ruby' then
300
+ define_native_tasks(for_platform, ruby_ver, @cross_compiling)
301
+ end
305
302
 
306
303
  # create cross task
307
304
  task 'cross' do
@@ -335,19 +332,15 @@ module Rake
335
332
  end
336
333
 
337
334
  def make
338
- RUBY_PLATFORM =~ /mswin/ ? 'nmake' : 'make'
339
- end
340
-
341
- def binary(platform = nil)
342
- ext = case platform
343
- when /darwin/
344
- 'bundle'
345
- when /mingw|mswin|linux/
346
- 'so'
347
- else
348
- RbConfig::CONFIG['DLEXT']
335
+ unless @make
336
+ @make =
337
+ if RUBY_PLATFORM =~ /mswin/ then
338
+ 'nmake'
339
+ else
340
+ ENV['MAKE'] || %w[gmake make].find { |c| system(c, '-v') }
341
+ end
349
342
  end
350
- "#{@name}.#{ext}"
343
+ @make
351
344
  end
352
345
 
353
346
  def source_files
@@ -372,5 +365,6 @@ module Rake
372
365
  end
373
366
  FAKE_RB
374
367
  end
368
+
375
369
  end
376
370
  end
@@ -0,0 +1,225 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rake/baseextensiontask'
4
+
5
+ # Define a series of tasks to aid in the compilation of Java extensions for
6
+ # gem developer/creators.
7
+
8
+ module Rake
9
+ class JavaExtensionTask < BaseExtensionTask
10
+
11
+ attr_accessor :classpath
12
+ attr_accessor :debug
13
+
14
+ # Provide source compatibility with specified release
15
+ attr_accessor :source_version
16
+
17
+ # Generate class files for specific VM version
18
+ attr_accessor :target_version
19
+
20
+ def platform
21
+ @platform ||= 'java'
22
+ end
23
+
24
+ def java_compiling(&block)
25
+ @java_compiling = block if block_given?
26
+ end
27
+
28
+ def init(name = nil, gem_spec = nil)
29
+ super
30
+ @source_pattern = '**/*.java'
31
+ @classpath = nil
32
+ @java_compiling = nil
33
+ @debug = false
34
+ @source_version = '1.5'
35
+ @target_version = '1.5'
36
+ end
37
+
38
+ def define
39
+ super
40
+
41
+ define_java_platform_tasks
42
+ end
43
+
44
+ private
45
+ def define_compile_tasks(for_platform = nil, ruby_ver = RUBY_VERSION)
46
+ # platform usage
47
+ platf = for_platform || platform
48
+
49
+ # lib_path
50
+ lib_path = lib_dir
51
+
52
+ # tmp_path
53
+ tmp_path = "#{@tmp_dir}/#{platf}/#{@name}"
54
+
55
+ # cleanup and clobbering
56
+ CLEAN.include(tmp_path)
57
+ CLOBBER.include("#{lib_path}/#{binary(platf)}")
58
+ CLOBBER.include("#{@tmp_dir}")
59
+
60
+ # directories we need
61
+ directory tmp_path
62
+ directory lib_dir
63
+
64
+ # copy binary from temporary location to final lib
65
+ # tmp/extension_name/extension_name.{so,bundle} => lib/
66
+ task "copy:#{@name}:#{platf}" => [lib_path, "#{tmp_path}/#{binary(platf)}"] do
67
+ cp "#{tmp_path}/#{binary(platf)}", "#{lib_path}/#{binary(platf)}"
68
+ end
69
+
70
+ not_jruby_compile_msg = <<-EOF
71
+ WARNING: You're cross-compiling a binary extension for JRuby, but are using
72
+ another interpreter. If your Java classpath or extension dir settings are not
73
+ correctly detected, then either check the appropriate environment variables or
74
+ execute the Rake compilation task using the JRuby interpreter.
75
+ (e.g. `jruby -S rake compile:java`)
76
+ EOF
77
+ warn_once(not_jruby_compile_msg) unless defined?(JRUBY_VERSION)
78
+
79
+ file "#{tmp_path}/#{binary(platf)}" => "#{tmp_path}/.build" do
80
+
81
+ class_files = FileList["#{tmp_path}/**/*.class"].
82
+ gsub("#{tmp_path}/", '')
83
+
84
+ # avoid environment variable expansion using backslash
85
+ class_files.gsub!('$', '\$') unless windows?
86
+
87
+ args = class_files.map { |path|
88
+ ["-C #{tmp_path}", path]
89
+ }.flatten
90
+
91
+ sh "jar cf #{tmp_path}/#{binary(platf)} #{args.join(' ')}"
92
+ end
93
+
94
+ file "#{tmp_path}/.build" => [tmp_path] + source_files do
95
+ classpath_arg = java_classpath_arg(@classpath)
96
+ debug_arg = @debug ? '-g' : ''
97
+
98
+ sh "javac #{java_extdirs_arg} -target #{@target_version} -source #{@source_version} -Xlint:unchecked #{debug_arg} #{classpath_arg} -d #{tmp_path} #{source_files.join(' ')}"
99
+
100
+ # Checkpoint file
101
+ touch "#{tmp_path}/.build"
102
+ end
103
+
104
+ # compile tasks
105
+ unless Rake::Task.task_defined?('compile') then
106
+ desc "Compile all the extensions"
107
+ task "compile"
108
+ end
109
+
110
+ # compile:name
111
+ unless Rake::Task.task_defined?("compile:#{@name}") then
112
+ desc "Compile #{@name}"
113
+ task "compile:#{@name}"
114
+ end
115
+
116
+ # Allow segmented compilation by platform (open door for 'cross compile')
117
+ task "compile:#{@name}:#{platf}" => ["copy:#{@name}:#{platf}"]
118
+ task "compile:#{platf}" => ["compile:#{@name}:#{platf}"]
119
+
120
+ # Only add this extension to the compile chain if current
121
+ # platform matches the indicated one.
122
+ if platf == RUBY_PLATFORM then
123
+ # ensure file is always copied
124
+ file "#{lib_path}/#{binary(platf)}" => ["copy:#{name}:#{platf}"]
125
+
126
+ task "compile:#{@name}" => ["compile:#{@name}:#{platf}"]
127
+ task "compile" => ["compile:#{platf}"]
128
+ end
129
+ end
130
+
131
+ def define_java_platform_tasks
132
+ # lib_path
133
+ lib_path = lib_dir
134
+
135
+ if @gem_spec && !Rake::Task.task_defined?("java:#{@gem_spec.name}")
136
+ task "java:#{@gem_spec.name}" do |t|
137
+
138
+ # FIXME: truly duplicate the Gem::Specification
139
+ spec = gem_spec.dup
140
+
141
+ # adjust to specified platform
142
+ spec.platform = Gem::Platform.new('java')
143
+
144
+ # clear the extensions defined in the specs
145
+ spec.extensions.clear
146
+
147
+ # add the binaries that this task depends on
148
+ ext_files = []
149
+
150
+ # go through native prerequisites and grab the real extension files from there
151
+ t.prerequisites.each do |ext|
152
+ ext_files << ext
153
+ end
154
+
155
+ # include the files in the gem specification
156
+ spec.files += ext_files
157
+
158
+ # expose gem specification for customization
159
+ if @java_compiling
160
+ @java_compiling.call(spec)
161
+ end
162
+
163
+ # Generate a package for this gem
164
+ gem_package = Rake::GemPackageTask.new(spec) do |pkg|
165
+ pkg.need_zip = false
166
+ pkg.need_tar = false
167
+ end
168
+ end
169
+
170
+ # add binaries to the dependency chain
171
+ task "java:#{@gem_spec.name}" => ["#{lib_path}/#{binary(platform)}"]
172
+
173
+ # ensure the extension get copied
174
+ unless Rake::Task.task_defined?("#{lib_path}/#{binary(platform)}") then
175
+ file "#{lib_path}/#{binary(platform)}" => ["copy:#{name}:#{platform}"]
176
+ end
177
+
178
+ task 'java' => ["java:#{@gem_spec.name}"]
179
+ end
180
+
181
+ task 'java' do
182
+ task 'compile' => 'compile:java'
183
+ end
184
+ end
185
+
186
+ #
187
+ # Discover Java Extension Directories and build an extdirs argument
188
+ #
189
+ def java_extdirs_arg
190
+ extdirs = Java::java.lang.System.getProperty('java.ext.dirs') rescue nil
191
+ extdirs = ENV['JAVA_EXT_DIR'] unless extdirs
192
+ java_extdir = extdirs.nil? ? "" : "-extdirs \"#{extdirs}\""
193
+ end
194
+
195
+ #
196
+ # Discover the Java/JRuby classpath and build a classpath argument
197
+ #
198
+ # @params
199
+ # *args:: Additional classpath arguments to append
200
+ #
201
+ # Copied verbatim from the ActiveRecord-JDBC project. There are a small myriad
202
+ # of ways to discover the Java classpath correctly.
203
+ #
204
+ def java_classpath_arg(*args)
205
+ jruby_cpath = nil
206
+ if RUBY_PLATFORM =~ /java/
207
+ begin
208
+ cpath = Java::java.lang.System.getProperty('java.class.path').split(File::PATH_SEPARATOR)
209
+ cpath += Java::java.lang.System.getProperty('sun.boot.class.path').split(File::PATH_SEPARATOR)
210
+ jruby_cpath = cpath.compact.join(File::PATH_SEPARATOR)
211
+ rescue => e
212
+ end
213
+ end
214
+ unless jruby_cpath
215
+ jruby_cpath = ENV['JRUBY_PARENT_CLASSPATH'] || ENV['JRUBY_HOME'] &&
216
+ Dir.glob("#{File.expand_path(ENV['JRUBY_HOME'])}/lib/*.jar").
217
+ join(File::PATH_SEPARATOR)
218
+ end
219
+ raise "JRUBY_HOME or JRUBY_PARENT_CLASSPATH are not set" unless jruby_cpath
220
+ jruby_cpath += File::PATH_SEPARATOR + args.join(File::PATH_SEPARATOR) unless args.empty?
221
+ jruby_cpath ? "-cp \"#{jruby_cpath}\"" : ""
222
+ end
223
+
224
+ end
225
+ end