begin require 'rjb' require 'rake' rescue LoadError require 'rubygems' require 'rjb' require 'rake' end require 'fileutils' module Jerbil IS_WINDOWS = RUBY_PLATFORM =~ /mswin|mingw/i JAVA_PATH_SEPARATOR = File::PATH_SEPARATOR # The JavaHelper module provides common helper functionality needed across different # classes. module JavaHelper # Provides the block with an instance of java.io.PrintStream and returns # all text printed to it as ruby-typed string. def printStream_to_s(&block) # :yields: printstream yieldIO('java.io.PrintStream', block) end # Provides the block with an instance of java.io.PrintWriter and returns # all text printed to it as ruby-typed string. def printWriter_to_s(&block) # :yields: printwriter yieldIO('java.io.PrintWriter', block) end def yieldIO(klass, block) # :nodoc: out = Rjb::import('java.io.ByteArrayOutputStream').new ps = Rjb::import(klass.to_s).new_with_sig 'Ljava.io.OutputStream;', out block.call(ps) ps.flush st = Rjb::import('java.lang.String').new_with_sig '[B', out.toByteArray String.new(st.toString) end # Returns an empty instance of java.util.List. def empty_list Rjb::import('java.util.ArrayList').new end # Returns an instance of java.util.List, populated with all # string found in +strings+. def str_list(strings) l = empty_list strings.each { |s| l.add s.to_s } l end # Serializes +obj+ using standard Java serialization. The result # is returned as bytearray. def serialize(obj) byteoos = Rjb::import('java.io.ByteArrayOutputStream').new oosKlass = Rjb::import('java.io.ObjectOutputStream') oos = oosKlass.new_with_sig 'Ljava.io.OutputStream;', byteoos begin oos.writeObject(obj) ensure oos.close end byteoos.toByteArray end # do a block, with a given value of a java system property set... # sets up the value of the system property before calling the supplied # block, then restores the def with_system_property( property_name, property_value, &a_proc ) if property_name system_class = Rjb::import( 'java.lang.System' ) property_value_save = system_class.getProperty( property_name ) begin system_class.setProperty( property_name , property_value ) a_proc.call ensure if property_value_save system_class.setProperty( property_name , property_value_save ) else system_class.clearProperty( property_name ) end end else a_proc.call end end # Loads the java virtual machine. This method should only be invoked once, typically # before task definitions in a Rakefile. If the environment variable +JAVA_OPTS+ is # set, it will be treated as extra parameter for the initial VM load. # # +classpath+:: a Rake::FileList containing the initial classpath. # +build_dir+:: an optional directory (or list of directories) which will be used to resolve classes at runtime. # Available options: # +java_home+:: JDK path (defaults to ENV['JAVA_HOME']) # +java_opts+:: additional JVM arguments (defaults to ENV['JAVA_OPTS']) # +loggingprops+:: the location of a java.util.logging configuration file. # +enableassert+:: wheter to enable assertions (default: enabled) # # ==Example # # load_jvm(FileList.new("lib/*.jar"), "build", :loggingprops => "logging.properties") # def JavaHelper.load_jvm(classpath, build_dir = nil, options = {} ) $jerbil_debug = ENV['JERBIL_DEBUG'] defaultopts = { :enableassert => true } options = defaultopts.merge(options.dup) #need verbose java exceptions $VERBOSE = true guess_java_home java_home = options[:java_home] || ENV['JAVA_HOME'] puts "using JDK in #{java_home}" if Rake.application.options.trace #include custom classloader classpath.unshift(File.join(File.dirname(__FILE__), "../../classloader")) if build_dir #include tools.jar from JDK (needed for javac etc.) classpath.unshift(File.join(java_home, "lib", "tools.jar")) if java_home jvmargs = [] jvmargs << "-ea" if options[:enableassert] jvmargs << "-Djava.util.logging.config.file=#{options[:loggingprops].to_s}" if options[:loggingprops] if ENV['JAVA_DEBUG'] suspend = ENV['JAVA_DEBUG'].to_s.index('suspend') ? 'y' : 'n' port = 8000 if ENV['JAVA_DEBUG'] =~ /port=([0-9]+)/ port = $1 end jvmargs += [ "-Xdebug", "-Xnoagent", "-Xrunjdwp:transport=dt_socket,address=#{port},server=y,suspend=#{suspend}" ] end if build_dir jerbil_debug = $jerbil_debug ? 'true' : 'false' jvmargs += [ "-Djava.system.class.loader=JerbilClassLoader", "-Djerbil.build.root=#{build_dir.to_a.join(':')}", "-Djerbil.debug=#{jerbil_debug}" ] else $stderr << "jerbil: build_dir not set: dynamic classloading is disabled\n" if Rake.application.options.trace end java_opts = (ENV['JAVA_OPTS'].split if ENV['JAVA_OPTS']) || options[:java_opts] jvmargs.unshift(java_opts) if java_opts if $jerbil_debug $stderr << "jvmargs : #{jvmargs.inspect}\n" $stderr << "initial cp: #{classpath.to_cp}\n" end begin Rjb::load(classpath.to_cp, jvmargs.flatten) rescue $stderr << "could not load java vm: make sure JAVA_HOME is set correctly!\n" raise end end # Tries to guess the JDK path if JAVA_HOME is not set (Windows only). def JavaHelper.guess_java_home # :nodoc: if ENV['JAVA_HOME'].nil? && IS_WINDOWS begin require 'win32/registry' Win32::Registry::HKEY_LOCAL_MACHINE.open('SOFTWARE\JavaSoft\Java Development Kit\1.5') do |reg| ENV['JAVA_HOME'] = reg['JavaHome'] end rescue end end end end # module JavaHelper ###################################################################### # Tasks including this module can easily specify additional # Java-style arguments (like -verbose, -gc). # # == Example # # Jerbil::MyExtraArgumentTakingTask.new do |t| # t.source = '1.5' # t.options :wibble, :wobble # end # # results in a command line of the form "-source 1.5 -wibble -wobble". module ExtraArgumentTaking def self.append_features(base) super class << base def create_alias_for(actual, new) @@aliases ||= {} @@aliases[new.to_s] = actual.to_s end end end attr_reader :extra_args def options(*args) args.each {|a| self.send(a)} end def add_files(files) add_extra_args files.to_a end def add_extra_args(*args) @extra_args = [] if extra_args.nil? @extra_args += args.flatten end def method_missing(symbol, *args) arg = symbol.to_s.sub(/=/, "") if @@aliases && @@aliases.has_key?(arg) arg = @@aliases[arg] end add_extra_args "-#{arg}", args end end ###################################################################### # A JavaFileList is a specialisation of a standard Rake::FileList. # It includes additional methods to deal with build dirs, resources # and other Java related things. class JavaFileList < Rake::FileList attr_reader :srcdir attr_reader :resdir attr_reader :dstdir attr_accessor :resource_patterns # srcdir:: the directory containing the Java source files. # dstdir:: destination directory for class files (used by JavacTask). # resdir:: an additional directory containing resources (to be copied to destdir after compile, defaults to srcdir) # extensions:: a list of extensions to treat as resources. The default is to treat # all files not ending in .java as resources. def initialize(srcdir, dstdir, resdir = srcdir, extensions = nil) super([]) @srcdir = srcdir @dstdir = dstdir @resdir = resdir @resource_patterns = [] add_extensions(extensions) include(srcdir + "/**/*.java") end # Returns a list of all java source files formatted as Java class names. # For example src/org/foo/Baz -> org.foo.Baz. def to_classnames # remove the initial directory and separator paths = self.pathmap("%{^#{srcdir_quoted}/?,}X") paths.gsub!("/", ".") end # Returns a list of Java classes. This method uses Rjb::import to # load the specified classes into the virtual machine. def to_classes classnames = to_classnames classes = classnames.map {|name| Rjb::import(name)} classes.to_a end # Translates all java source files into their corresponding class destination # files, based on +dstdir+. # # For example src/org/foo/Baz.java -> classes/org/foo/Baz.class. def to_classfiles self.pathmap("%{^#{srcdir_quoted},#{dstdir}}X.class") end def sourcepath srcdir end # Returns a map containing all resources found in +resdir+ with their value # pointing to the destination file. # Resources are typically files in +srcdir+ with extensions other than .java # (properties, xml, ...). If you want to copy only specfic resources register # extensions using #add_extension. def resources res_list = FileList.new if resource_patterns.empty? res_list.include(resdir + "/**/*.*") else resource_patterns.each { |p| res_list.include(resdir+p) } end res_list.exclude(resdir + "/**/*.java") res_map = {} res_list.each do |r| res_map[r] = r.pathmap("%{^#{resdir_quoted},#{dstdir}}p") end res_map end # Calls block once for each resource found in +srcdir+, passing # the source and destination file as parameter. def resource_and_target # :yields: resource,target resources.each do |r,t| yield r, t if block_given? end end # Calls block once for each sourcefile in +srcdir+ with corresponding target # file. def source_and_target # :yields: src,target self.each do |file| yield file, file.pathmap("%{^#{srcdir_quoted},#{dstdir}}X.class") end end # Returns a list of out-of-date files, based on timestamp comparison. def out_of_date return self.to_a unless (File.exists?(dstdir) and Dir.entries(dstdir).length > 2) outofdate = [] source_and_target do |s,t| outofdate << s unless FileUtils.uptodate?(t,s) end outofdate end def uptodate? out_of_date.empty? end # Registers a resource extension. # # add_extension("xml") => all xml files # def add_extension(ext) @resource_patterns << "/**/*.#{ext}" end # Registers a list of extensions. def add_extensions(exts) exts.to_a.each {|ext| add_extension(ext)} end private def srcdir_quoted Regexp.quote(srcdir) end def resdir_quoted Regexp.quote(resdir) end end ###################################################################### # A MultiJavaFileList is a container object for holding several JavaFileList # objects. This is useful for multidirectory builds. # # == Example # SOURCE_DIR = "src" # MODULES = [ "a", "b" ] # JAVA_BUILD_DIR = "classes" # JAVA_FILES = MultiJavaFileList.new(MODULES, JAVA_BUILD_DIR, SOURCE_DIR) # # This will look for source files in a/src and b/src, # compiling into +classes+. class MultiJavaFileList attr_reader :modules, :dstdir def initialize(modules, dstdir, srcprefix = "src", copypat = nil) @java_files = [] @modules = modules @dstdir = dstdir modules.each do | m | srcdir = File.join(m, srcprefix) @java_files << JavaFileList.new(srcdir, dstdir, srcdir, copypat ) end end def sourcepath self.srcdir.join(JAVA_PATH_SEPARATOR) end def srcdir @java_files.collect{|jf| jf.srcdir} end def to_a files = [] @java_files.each do |f| files += f end files end def to_ary to_a end def resources res = {} @java_files.each do |f| res.update!(f.resources) end res end def resource_and_target @java_files.each do |jf| jf.resource_and_target do |r,t| yield r,t end end end def source_and_target @java_files.each do |jf| jf.source_and_target do |s,t| yield s,t end end end def uptodate? (@java_files.find { |jf| !jf.uptodate? }).nil? end def out_of_date f = [] @java_files.each { |jf| f += jf.out_of_date } f end def gsub!( replace, replace_with ) @java_files.each{ |f| f.gsub!( replace, replace_with ) } end def map!(&block) @java_files.each { |f| f.map!{ |g| block.call(g) } } end end end ###################################################################### # Extensions for standard rake classes. module Rake class FileList # Returns the filelist formatted as Java classpath. # ("/tmp/foo.jar:/tmp/baz.jar") def to_cp(sep = Jerbil::JAVA_PATH_SEPARATOR) self.join(sep) end end class TaskLib # Adds a dependency to the given task def depends_on(*args) dependencies.concat(args) end def dependencies @dependencies ||= [] end end end # make JavaFileList available to toplevel JavaFileList = Jerbil::JavaFileList # make load_jvm available to toplevel # See JavaHelper.load_jvm. def load_jvm(args, build_dir, options={}) Jerbil::JavaHelper.load_jvm(args, build_dir, options) end