lib/java/java.rb in buildr-0.18.0 vs lib/java/java.rb in buildr-0.19.0

- old
+ new

@@ -1,152 +1,310 @@ -require 'stringio' +require "rjb" +require "core/project" module Buildr + + # Base module for all things Java. module Java - JAVA_OPTIONS = [ :verbose, :noop, :cp, :classpath, :name, :java_args ] + # Options accepted by #java and other methods here. + JAVA_OPTIONS = [ :verbose, :classpath, :name, :java_args ] + # Classpath dependencies available when running JUnit. JUNIT_REQUIRES = [ "junit:junit:jar:3.8.1", "jmock:jmock:jar:1.1.0" ] - def self.java(*args) - options = Hash === args.last ? args.pop : {} - options[:verbose] ||= Rake.application.options.trace || false - fu_check_options options, *JAVA_OPTIONS + # Returned by Java#rjb, you can use this object to set the RJB classpath, specify blocks to be invoked + # after loading RJB, and load RJB itself. + # + # RJB can be loaded exactly once, and once loaded, you cannot change its classpath. Of course you can + # call libraries that manage their own classpath, but the lazy way is to just tell RJB of all the + # classpath dependencies you need in advance, before loading it. + # + # For that reason, you should not load RJB until the moment you need it. You can call #load or call + # Java#rjb with a block. For the same reason, you may need to specify code to execute when loading + # (see #onload). + class RjbWrapper - name = options[:name] || "java #{args.first}" - cmd_args = [] - classpath = classpath_from(options) - cmd_args << "-cp" << classpath.join(File::PATH_SEPARATOR) unless classpath.empty? - cmd_args += options[:java_args].flatten if options[:java_args] - cmd_args += args.flatten.compact - cmd_args << { :verbose=>options[:verbose] } - unless options[:noop] - puts "Running #{name}" if verbose - sh(path_to_bin("java"), *cmd_args) { |ok, res| fail "Failed to execute #{name}, see errors above" unless ok } + include Singleton + + def initialize() #:nodoc: + @classpath = [] + @onload = [] + onload do + Rjb.load(artifacts(classpath).each { |task| task.invoke if task.respond_to?(:invoke) }. + map(&:to_s).join(File::PATH_SEPARATOR)) + end end - end - def self.apt(*args) - options = Hash === args.last ? args.pop : {} - options[:verbose] ||= Rake.application.options.trace || false - fu_check_options options, :verbose, :noop, :compile, :source, :output, :cp, :classpath + # The classpath used when loading RJB. + attr_accessor :classpath - files = args.collect { |arg| File.directory?(arg) ? FileList[File.join(arg, "**", "*.java")] : arg }.flatten - args = [ options[:verbose] ? "-verbose" : "-nowarn" ] - if options[:compile] - args << "-d" << options[:output] - else - args << "-nocompile" << "-s" << options[:output] + # :call-seq: + # onload { ... } + # + # Adds a block to call when loading RJB and returns self. + # + # You can only load RJB once, and you may need to do some tasks after the initial load. + # For example, the Ant module requires Antwrap which can only be loaded after RJB. + def onload(&block) + @onload << block + self end - args << "-source" << options[:source] if options[:source] - classpath = classpath_from(options) - args << "-cp" << classpath.join(File::PATH_SEPARATOR) unless classpath.empty? - args += files - args << { :verbose=>options[:verbose] } - unless options[:noop] - puts "Running apt" if verbose - sh(path_to_bin("apt"), *args) { |ok, res| fail "Failed to execute apt, see errors above" unless ok } + + # :call-seq: + # load() + # + # Loads RJB. You can also call Java#ejb with a block to get the same effect. + def load() + @onload.each(&:call) + @onload.clear end - end - # Gets the version of the java VM - def self.version() - @version ||= `java -version 2>&1`.scan(/java version "(.*)"/)[0][0] - end - - def self.tools() - unless @tools - tools = File.join(ENV['JAVA_HOME'], "lib", "tools.jar") - @tools = File.exist?(tools) ? tools : [] + def method_missing(sym, *args, &block) #:nodoc: + Rjb.send sym, *args, &block end - @tools end - class AptTask < Rake::FileTask + class << self - def initialize(*args) - super - enhance do |task| - Java.apt *task.prerequisites + [options.merge(:output=>task.name)] - end + # :call-seq: + # version() => string + # + # Returns the version number of the JVM. + # + # For example: + # puts Java.version + # => 1.5.0_10 + def version() + @version ||= `#{path_to_bin("java")} -version 2>&1`.scan(/java version "(.*)"/)[0][0] end - def options() - @options ||= {} + # :call-seq: + # tools_jar() => path + # + # Returns a path to tools.jar. + def tools_jar() + unless @tools + home = ENV["JAVA_HOME"] || File.dirname(File.dirname(`which java`.split.first)) + tools = File.join(home, "lib/tools.jar") + @tools = tools if File.exist?(tools) + end + @tools end - def using(options) - self.options.merge!(options) - self + # :call-seq: + # java(class, *args, options?) + # + # Runs Java with the specified arguments. + # + # The last argument may be a Hash with additional options: + # * :classpath -- One or more file names, tasks or artifact specifications. + # These are all expanded into artifacts, and all tasks are invoked. + # * :java_args -- Any additional arguments to pass (e.g. -hotspot, -xms) + # * :name -- Shows this name, otherwise shows the first argument (the class name). + # * :verbose -- If true, prints the command and all its argument. + def java(*args) + options = Hash === args.last ? args.pop : {} + options[:verbose] ||= Rake.application.options.trace || false + fu_check_options options, *JAVA_OPTIONS + + name = options[:name] || "java #{args.first}" + cmd_args = [] + classpath = classpath_from(options) + cmd_args << "-cp" << classpath.join(File::PATH_SEPARATOR) unless classpath.empty? + cmd_args += options[:java_args].flatten if options[:java_args] + cmd_args += args.flatten.compact + cmd_args << { :verbose=>options[:verbose] } + unless Rake.application.options.dryrun + puts "Running #{name}" if verbose + sh(path_to_bin("java"), *cmd_args) { |ok, res| fail "Failed to execute #{name}, see errors above" unless ok } + end end - end + # :call-seq: + # apt(*files, options) + # + # Runs Apt with the specified arguments. + # + # The last argument may be a Hash with additional options: + # * :compile -- If true, compile source files to class files. + # * :source -- Specifies source compatibility with a given JVM release. + # * :output -- Directory where to place the generated source files, or the + # generated class files when compiling. + # * :classpath -- One or more file names, tasks or artifact specifications. + # These are all expanded into artifacts, and all tasks are invoked. + # * :verbose -- If true, prints the command and all its argument and also runs + # Apt in verbose mode (the default is -nowarn). + def apt(*args) + options = Hash === args.last ? args.pop : {} + options[:verbose] ||= Rake.application.options.trace || false + fu_check_options options, :verbose, :compile, :source, :output, :classpath - def self.apt_task(args) - output = args.keys.first - files = args.values.first.collect { |f| File.directory?(f) ? FileList[f + "/**/*.java"] : f }.flatten - AptTask.define_task(output=>files) - end + files = args.flatten.map(&:to_s). + collect { |arg| File.directory?(arg) ? FileList["#{arg}/**/*.java"] : arg }.flatten + args = [ options[:verbose] ? "-verbose" : "-nowarn" ] + if options[:compile] + args << "-d" << options[:output].to_s + else + args << "-nocompile" << "-s" << options[:output].to_s + end + args << "-source" << options[:source] if options[:source] + classpath = classpath_from(options) + args << "-cp" << classpath.join(File::PATH_SEPARATOR) unless classpath.empty? + args += files + args << { :verbose=>options[:verbose] } + unless Rake.application.options.dryrun + puts "Running apt" if verbose + sh(path_to_bin("apt"), *args) { |ok, res| fail "Failed to execute apt, see errors above" unless ok } + end + end - def self.javac(*args) - options = Hash === args.last ? args.pop : {} - options[:verbose] ||= Rake.application.options.trace || false - fu_check_options options, :verbose, :noop, :cp, :classpath, :sourcepath, :output, :javac_args, :name + # :call-seq: + # javac(*files, options) + # + # Runs Javac with the specified arguments. + # + # The last argument may be a Hash with additional options: + # * :output -- Target directory for all compiled class files. + # * :classpath -- One or more file names, tasks or artifact specifications. + # These are all expanded into artifacts, and all tasks are invoked. + # * :sourcepath -- Additional source paths to use. + # * :javac_args -- Any additional arguments to pass (e.g. -extdirs, -encoding) + # * :name -- Shows this name, otherwise shows the working directory. + # * :verbose -- If true, prints the command and all its argument. + def javac(*args) + options = Hash === args.last ? args.pop : {} + options[:verbose] ||= Rake.application.options.trace || false + fu_check_options options, :verbose, :classpath, :sourcepath, :output, :javac_args, :name - files = args.flatten.each { |f| f.invoke if f.respond_to?(:invoke) }.map(&:to_s). - collect { |arg| File.directory?(arg) ? FileList[File.join(arg, "**", "*.java")] : arg }.flatten - name = options[:name] || Dir.pwd + files = args.flatten.each { |f| f.invoke if f.respond_to?(:invoke) }.map(&:to_s). + collect { |arg| File.directory?(arg) ? FileList["#{arg}/**/*.java"] : arg }.flatten + name = options[:name] || Dir.pwd - cmd_args = [] - classpath = classpath_from(options) - #classpath += options[:output] if options[:output] - cmd_args << "-cp" << classpath.join(File::PATH_SEPARATOR) unless classpath.empty? - cmd_args << "-sourcepath" << options[:sourcepath].join(File::PATH_SEPARATOR) if options[:sourcepath] - cmd_args << "-d" << options[:output] if options[:output] - cmd_args += options[:javac_args].flatten if options[:javac_args] - cmd_args += files - cmd_args << { :verbose=>options[:verbose] } - unless options[:noop] || files.empty? - puts "Compiling #{files.size} source files in #{name}" if verbose - sh(path_to_bin("javac"), *cmd_args) { |ok, res| fail "Failed to compile, see errors above" unless ok } + cmd_args = [] + classpath = classpath_from(options) + #classpath += options[:output] if options[:output] + cmd_args << "-cp" << classpath.join(File::PATH_SEPARATOR) unless classpath.empty? + cmd_args << "-sourcepath" << options[:sourcepath].join(File::PATH_SEPARATOR) if options[:sourcepath] + cmd_args << "-d" << options[:output].to_s if options[:output] + cmd_args += options[:javac_args].flatten if options[:javac_args] + cmd_args += files + cmd_args << { :verbose=>options[:verbose] } + unless Rake.application.options.dryrun + puts "Compiling #{files.size} source files in #{name}" if verbose + sh(path_to_bin("javac"), *cmd_args) { |ok, res| fail "Failed to compile, see errors above" unless ok } + end end - end - def self.junit(*args) - options = Hash === args.last ? args.pop : {} - options[:verbose] ||= Rake.application.options.trace || false - fu_check_options options, :verbose, :noop, :cp, :classpath, :sourcepath, :output, :javac_args, :name + # :call-seq: + # junit(*classes, options) => [ passed, failed ] + # + # Runs JUnit test cases from the specified classes. Returns an array with two lists, + # one containing the names of all classes that passes, the other containing the names + # of all classes that failed. + # + # The last argument may be a Hash with additional options: + # * :classpath -- One or more file names, tasks or artifact specifications. + # These are all expanded into artifacts, and all tasks are invoked. + # * :verbose -- If true, prints the command and all its argument. + def junit(*args) + options = Hash === args.last ? args.pop : {} + options[:verbose] ||= Rake.application.options.trace || false + fu_check_options options, :verbose, :classpath - classpath = classpath_from(options) + junit_artifacts - tests = args.flatten - failed = tests.inject([]) do |failed, test| - begin - java "junit.textui.TestRunner", test, :classpath=>classpath, :name=>"tests in #{test}" - failed - rescue - failed << test + classpath = classpath_from(options) + junit_artifacts + tests = args.flatten + failed = tests.inject([]) do |failed, test| + begin + java "junit.textui.TestRunner", test, :classpath=>classpath, :name=>"tests in #{test}", :verbose=>options[:verbose] + failed + rescue + failed << test + end end + [ tests - failed, failed ] end - [ tests - failed, failed ] - end + # :call-seq: + # rjb() => RjbWrapper + # rjb() { ... } + # + # This method can be used in two ways. Without a block, returns the RjbWrapper + # object which you can use to configure the RJB classpath or call other RJB methods. + # With a block, loads RJB and yields to the block, returning its result. + # + # For example: + # Java.rjb.classpath += REQUIRES + # Java.rjb.onload { require "antwrap" } + # . . . + # + # def execute(name, options) + # options = options.merge(:name=>name, :base_dir=>Dir.pwd, :declarative=>true) + # Java.rjb { AntProject.new(options) } + # end + def rjb() + if block_given? + RjbWrapper.instance.load + yield + else + RjbWrapper.instance + end + end + + # :call-seq: + # path_to_bin(cmd?) => path + # + # Returns the path to the specified Java command (with no argument to java itself). + # Uses JAVA_HOME if set, otherwise assumes the command is accessible from the path. + def path_to_bin(name = "java") + ENV["JAVA_HOME"] ? File.join(ENV["JAVA_HOME"], "bin", name) : name + end + protected - def self.path_to_bin(name = "java") - ENV["JAVA_HOME"] ? File.join(ENV["JAVA_HOME"], "bin", name) : name + # :call-seq: + # classpath_from(options) => files + # + # Extracts the classpath from the options, expands it by calling artifacts, invokes + # each of the artifacts and returns an array of paths. + def classpath_from(options) + classpath = (options[:classpath] || []).collect + artifacts(classpath).each { |t| t.invoke if t.respond_to?(:invoke) }.map(&:to_s) + end + + # :call-seq: + # junit_artifacts() => files + # + # Returns the JUnit artifacts as paths, after downloading and installing them (if necessary). + def junit_artifacts() + @junit_artifacts ||= artifacts(JUNIT_REQUIRES).each { |task| task.invoke }.map(&:to_s) + end + end - def self.classpath_from(options) - classpath = (options[:classpath] || []).collect | (options[:cp] || []).collect - artifacts(classpath).each { |t| t.invoke if t.respond_to?(:invoke) }.map(&:to_s) + # See Java#java. + def java(*args) + Java.java(*args) end - def self.junit_artifacts() - @junit_artifacts ||= artifacts(JUNIT_REQUIRES).each { |task| task.invoke } + # :call-seq: + # apt(*sources) => task + # + # Returns a task that will use Java#apt to generate source files in target/generated/apt, + # from all the source directories passed as arguments. Uses the compile.sources list if + # on arguments supplied. + # + # For example: + # + def apt(*sources) + sources = compile.sources if sources.empty? + file(path_to("target/generated/apt")=>sources) do |task| + Java.apt(sources.map(&:to_s) - [task.name], :output=>task.name, + :classpath=>compile.classpath, :source=>compile.options.source) + end end end - def java(*args) - Java.java(*args) + class Project + include Java end - end