Permalink
Fetching contributors…
Cannot retrieve contributors at this time
225 lines (178 sloc) 7.03 KB
require 'rake/baseextensiontask'
# Define a series of tasks to aid in the compilation of Java extensions for
# gem developer/creators.
module Rake
class JavaExtensionTask < BaseExtensionTask
attr_accessor :classpath
attr_accessor :debug
# Provide source compatibility with specified release
attr_accessor :source_version
# Generate class files for specific VM version
attr_accessor :target_version
def platform
@platform ||= 'java'
end
def java_compiling(&block)
@java_compiling = block if block_given?
end
def init(name = nil, gem_spec = nil)
super
@source_pattern = '**/*.java'
@classpath = nil
@java_compiling = nil
@debug = false
@source_version = '1.5'
@target_version = '1.5'
end
def define
super
define_java_platform_tasks
end
private
def define_compile_tasks(for_platform = nil, ruby_ver = RUBY_VERSION)
# platform usage
platf = for_platform || platform
# lib_path
lib_path = lib_dir
# tmp_path
tmp_path = "#{@tmp_dir}/#{platf}/#{@name}"
# cleanup and clobbering
CLEAN.include(tmp_path)
CLOBBER.include("#{lib_path}/#{binary(platf)}")
CLOBBER.include("#{@tmp_dir}")
# directories we need
directory tmp_path
directory lib_dir
# copy binary from temporary location to final lib
# tmp/extension_name/extension_name.{so,bundle} => lib/
task "copy:#{@name}:#{platf}" => [lib_path, "#{tmp_path}/#{binary(platf)}"] do
install "#{tmp_path}/#{binary(platf)}", "#{lib_path}/#{binary(platf)}"
end
file "#{tmp_path}/#{binary(platf)}" => "#{tmp_path}/.build" do
class_files = FileList["#{tmp_path}/**/*.class"].
gsub("#{tmp_path}/", '')
# avoid environment variable expansion using backslash
class_files.gsub!('$', '\$') unless windows?
args = class_files.map { |path|
["-C #{tmp_path}", path]
}.flatten
sh "jar cf #{tmp_path}/#{binary(platf)} #{args.join(' ')}"
end
file "#{tmp_path}/.build" => [tmp_path] + source_files do
not_jruby_compile_msg = <<-EOF
WARNING: You're cross-compiling a binary extension for JRuby, but are using
another interpreter. If your Java classpath or extension dir settings are not
correctly detected, then either check the appropriate environment variables or
execute the Rake compilation task using the JRuby interpreter.
(e.g. `jruby -S rake compile:java`)
EOF
warn_once(not_jruby_compile_msg) unless defined?(JRUBY_VERSION)
classpath_arg = java_classpath_arg(@classpath)
debug_arg = @debug ? '-g' : ''
sh "javac #{java_extdirs_arg} -target #{@target_version} -source #{@source_version} -Xlint:unchecked #{debug_arg} #{classpath_arg} -d #{tmp_path} #{source_files.join(' ')}"
# Checkpoint file
touch "#{tmp_path}/.build"
end
# compile tasks
unless Rake::Task.task_defined?('compile') then
desc "Compile all the extensions"
task "compile"
end
# compile:name
unless Rake::Task.task_defined?("compile:#{@name}") then
desc "Compile #{@name}"
task "compile:#{@name}"
end
# Allow segmented compilation by platform (open door for 'cross compile')
task "compile:#{@name}:#{platf}" => ["copy:#{@name}:#{platf}"]
task "compile:#{platf}" => ["compile:#{@name}:#{platf}"]
# Only add this extension to the compile chain if current
# platform matches the indicated one.
if platf == RUBY_PLATFORM then
# ensure file is always copied
file "#{lib_path}/#{binary(platf)}" => ["copy:#{name}:#{platf}"]
task "compile:#{@name}" => ["compile:#{@name}:#{platf}"]
task "compile" => ["compile:#{platf}"]
end
end
def define_java_platform_tasks
# lib_path
lib_path = lib_dir
if @gem_spec && !Rake::Task.task_defined?("java:#{@gem_spec.name}")
task "java:#{@gem_spec.name}" do |t|
# FIXME: workaround Gem::Specification limitation around cache_file:
# http://github.com/rubygems/rubygems/issues/78
spec = gem_spec.dup
spec.instance_variable_set(:"@cache_file", nil) if spec.respond_to?(:cache_file)
# adjust to specified platform
spec.platform = Gem::Platform.new('java')
# clear the extensions defined in the specs
spec.extensions.clear
# add the binaries that this task depends on
ext_files = []
# go through native prerequisites and grab the real extension files from there
t.prerequisites.each do |ext|
ext_files << ext
end
# include the files in the gem specification
spec.files += ext_files
# expose gem specification for customization
if @java_compiling
@java_compiling.call(spec)
end
# Generate a package for this gem
Gem::PackageTask.new(spec) do |pkg|
pkg.need_zip = false
pkg.need_tar = false
end
end
# add binaries to the dependency chain
task "java:#{@gem_spec.name}" => ["#{lib_path}/#{binary(platform)}"]
# ensure the extension get copied
unless Rake::Task.task_defined?("#{lib_path}/#{binary(platform)}") then
file "#{lib_path}/#{binary(platform)}" => ["copy:#{name}:#{platform}"]
end
task 'java' => ["java:#{@gem_spec.name}"]
end
task 'java' do
task 'compile' => 'compile:java'
end
end
#
# Discover Java Extension Directories and build an extdirs argument
#
def java_extdirs_arg
extdirs = Java::java.lang.System.getProperty('java.ext.dirs') rescue nil
extdirs = ENV['JAVA_EXT_DIR'] unless extdirs
java_extdir = extdirs.nil? ? "" : "-extdirs \"#{extdirs}\""
end
#
# Discover the Java/JRuby classpath and build a classpath argument
#
# @params
# *args:: Additional classpath arguments to append
#
# Copied verbatim from the ActiveRecord-JDBC project. There are a small myriad
# of ways to discover the Java classpath correctly.
#
def java_classpath_arg(*args)
jruby_cpath = nil
if RUBY_PLATFORM =~ /java/
begin
cpath = Java::java.lang.System.getProperty('java.class.path').split(File::PATH_SEPARATOR)
cpath += Java::java.lang.System.getProperty('sun.boot.class.path').split(File::PATH_SEPARATOR)
jruby_cpath = cpath.compact.join(File::PATH_SEPARATOR)
rescue => e
end
end
unless jruby_cpath
jruby_cpath = ENV['JRUBY_PARENT_CLASSPATH'] || ENV['JRUBY_HOME'] &&
Dir.glob("#{File.expand_path(ENV['JRUBY_HOME'])}/lib/*.jar").
join(File::PATH_SEPARATOR)
end
raise "JRUBY_HOME or JRUBY_PARENT_CLASSPATH are not set" unless jruby_cpath
jruby_cpath += File::PATH_SEPARATOR + args.join(File::PATH_SEPARATOR) unless args.empty?
jruby_cpath ? "-cp \"#{jruby_cpath}\"" : ""
end
end
end