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

- old
+ new

@@ -1,11 +1,34 @@ +require "core/project" +require "core/build" +require "java/artifact" +require "java/java" +require "tasks/filter" + module Buildr module Java + # Wraps Javac in a task that does all the heavy lifting. + # + # Accepts multiple source directories that are invoked as prerequisites before compilation. + # You can pass a task as a source directory, e.g. compile.from(apt). + # + # Likewise, classpath dependencies are invoked before compiling. All classpath dependencies + # are evaluated as #artifacts, so you can pass artifact specifications and even projects. + # + # Creates a file task for the target directory, so executing that task as a dependency will + # execute the compile task first. + # + # Compiler options are inherited form a parent task, e.g. the foo:bar:compile task inherits + # its options from the foo:compile task. Even if foo is an empty project that does not compile + # any classes itself, you can use it to set compile options for all its sub-projects. + # + # Normally, the project will take care of setting the source and target directory, and you + # only need to set options and classpath dependencies. See Project#compile. class CompileTask < Rake::Task - # Compiler options, accessible from Compiler#options. + # Compiler options, accessible from CompileTask#options. # # Supported options are: # - warnings -- Generate warnings if true (opposite of -nowarn). # - deprecation -- Output source locations where deprecated APIs are used. # - source -- Source compatibility with specified release. @@ -14,41 +37,48 @@ # options, or pass a specific setting as string or array of strings. # - debug -- Generate debugging info. # - other -- Array of options to pass to the Java compiler as is. # # For example: - # options.warnings = true - # options.source = options.target = "1.6" + # compile.options.warnings = true + # compile.options.source = options.target = "1.6" class Options - include Attributes + include InheritedAttributes OPTIONS = [:warnings, :deprecation, :source, :target, :lint, :debug, :other] # Generate warnings (opposite of -nowarn). + attr_accessor :warnings inherited_attr :warnings # Output source locations where deprecated APIs are used. + attr_accessor :deprecation inherited_attr :deprecation # Provide source compatibility with specified release. + attr_accessor :source inherited_attr :source # Generate class files for specific VM version. + attr_accessor :target inherited_attr :target # Values to pass to Xlint: string or array. Use true to enable # Xlint with no values. + attr_accessor :lint inherited_attr :lint # Generate all debugging info. + attr_accessor :debug inherited_attr :debug # Array of arguments passed to the Java compiler as is. + attr_accessor :other inherited_attr :other - def initialize(parent = nil) + def initialize(parent = nil) #:nodoc: @parent = parent end - # :nodoc: - attr_reader :parent + attr_reader :parent # :nodoc: + # Resets all the options. def clear() OPTIONS.each { |name| send "#{name}=", nil } end def to_s() @@ -77,11 +107,11 @@ end end - def initialize(*args) + def initialize(*args) #:nodoc: super parent = Rake::Task["^compile"] if name[":"] # Only if in namespace if parent && parent.respond_to?(:options) @options = Options.new(parent.options) else @@ -89,211 +119,261 @@ end @sources = [] @classpath = [] enhance do |task| - mkpath target, :verbose=>false + mkpath target.to_s, :verbose=>false Java.javac source_files.keys, :sourcepath=>sources.map(&:to_s).select { |source| File.directory?(source) }.uniq, :classpath=>classpath, :output=>target, :javac_args=>options.javac_args, :name=>task.name # By touching the target we let other tasks know we did something, # and also prevent recompiling again for classpath dependencies. - touch target, :verbose=>false + touch target.to_s, :verbose=>false end end # Source directories and files to compile. attr_accessor :sources + # :call-seq: + # from(*sources) => self + # # Adds source directories and files to compile, and returns self. # # For example: # compile.from("src/java").into("classes").with("module1.jar") - def from(*files) - @sources |= files.flatten + def from(*sources) + @sources |= sources.flatten self end # Classpath dependencies. attr_accessor :classpath + # :call-seq: + # with(*artifacts) => self + # # Adds files and artifacts as classpath dependencies, and returns self. # # Calls #artifacts on the arguments, so you can pass artifact specifications, # tasks, projects, etc. Use this rather than setting the classpath directly. # # For example: # compile.with("module1.jar", "log4j:log4j:jar:1.0", project("foo")) - def with(*files) - @classpath |= artifacts(files.flatten).uniq + def with(*specs) + @classpath |= artifacts(specs.flatten).uniq self end # The target directory for the generated class files. attr_reader :target + # :call-seq: + # into(path) => self + # # Sets the target directory and returns self. This will also set the compile task # as a prerequisite to a file task on the target directory. # # For example: # compile(src_dir).into(target_dir).with(artifacts) - def into(dir) - dir = File.expand_path(dir) - unless @target == dir - @target = dir - file(dir).enhance [self] - end + # Both compile.invoke and file(target_dir).invoke will compile the source files. + def into(path) + path = File.expand_path(path.to_s) + @target = file(path).enhance([self]) unless @target && @target.to_s == path self end # Returns the compiler options. attr_reader :options + # :call-seq: + # using(options) => self + # # Sets the compiler options from a hash and returns self. # # For example: # compile.using(:warnings=>true, :source=>"1.5") def using(hash) hash.each { |k, v| options.send "#{k}=", v } self end - def timestamp() + def timestamp() #:nodoc: # If we compiled successfully, then the target directory reflects that. # If we didn't, see needed? - @timestamp ||= (File.exist?(target) ? File.stat(target).mtime : Rake::EARLY) + target ? target.timestamp : Rake::EARLY end - def needed?() + def needed?() #:nodoc: return false if source_files.empty? - return true unless File.exist?(target) + return true unless File.exist?(target.to_s) return true if source_files.any? { |j, c| !File.exist?(c) || File.stat(j).mtime > File.stat(c).mtime } oldest = source_files.map { |j, c| File.stat(c).mtime }.min return classpath.any? { |path| application[path].timestamp > oldest } end - def prerequisites() + def prerequisites() #:nodoc: super + classpath + sources end - def invoke_prerequisites() + def invoke_prerequisites() #:nodoc: prerequisites.each { |n| application[n, @scope].invoke } end - protected - # Returns the files to compile. This list is derived from the list of sources, # expanding directories into files, and includes only source files that are # newer than the corresponding class file. Includes all files if one or more # classpath dependency has been updated. def source_files() @source_files ||= @sources.map(&:to_s).inject({}) do |map, source| raise "Compile task #{name} has source files, but no target directory" unless target + target_dir = target.to_s if File.directory?(source) base = Pathname.new(source) - FileList[File.join(source, "**", "*.java")]. - each { |file| map[file] = File.join(target, Pathname.new(file).relative_path_from(base).to_s.ext(".class")) } + FileList["#{source}/**/*.java"]. + each { |file| map[file] = File.join(target_dir, Pathname.new(file).relative_path_from(base).to_s.ext(".class")) } else - map[source] = File.join(target, File.basename(source).ext(".class")) + map[source] = File.join(target_dir, File.basename(source).ext(".class")) end map end end end + + # The resources task is executed by the compile task to copy resource files over + # to the target directory. You can enhance this task in the normal way, but mostly + # you will use the task's filter. + # + # For example: + # resources.filter.using "Copyright"=>"Acme Inc, 2007" + class ResourcesTask < Rake::Task + + # Returns the filter used to copy resources over. See Buildr::Filter. + attr_reader :filter + + def initialize(*args) #:nodoc: + super + @filter = Buildr::filter + enhance { filter.run } + end + + # :call-seq: + # include(*files) => self + # + # Includes the specified files in the filter and returns self. + def include(*files) + filter.include *files + self + end + + # :call-seq: + # exclude(*files) => self + # + # Excludes the specified files in the filter and returns self. + def exclude(*files) + filter.exclude *files + self + end + + def prerequisites() #:nodoc: + super + filter.sources + end + + end + end # Local task to execute the compile task of the current project. # This task is not itself a compile task. desc "Compile all projects" - Project.local_task(task("compile")) + Project.local_task("compile") { |name| "Compiling #{name}" } class Project - # The source directory. The default is "src". - inherited_attr :src_dir, "src" - # The Java source code directory. The default is <src_dir>/main/java. - inherited_attr :java_src_dir do File.join(src_dir, "main", "java") end - # The resources source directory. The default is <src_dir>/main/resources. - inherited_attr :resources_dir do File.join(src_dir, "main", "resources") end - # The target directory. The default is "target". - inherited_attr :target_dir, "target" - # The Java target directory. The default is <target_dir>/classes. - inherited_attr :java_target_dir do File.join(target_dir, "classes") end - + # :call-seq: + # prepare(*prereqs) => task + # prepare(*prereqs) { |task| .. } => task + # # The prepare task executes before the #compile task. Use it for pre-compilation # tasks, e.g. generating source code. # # This method returns the project's prepare task. It also accepts a list of # prerequisites and a block, used to enhance the prepare task. # - # By default the prepare task will only generate the #target_dir directory. - # # For example: - # prepare "src/generated" - # prepare { javacc.run } - def prepare(*tasks, &block) - task("prepare").enhance tasks, &block + # prepare { schema_to_java } + def prepare(*prereqs, &block) + task("prepare").enhance prereqs, &block end + # :call-seq: + # compile(*sources) => CompileTask + # compile(*sources) { |task| .. } => CompileTask + # # The compile task does what its name suggests. This method returns the project's - # compile task. It also accepts a list of source directories and files to compile - # (equivalent to calling Java::CompileTask#from on the task), and a block for any + # CompileTask. It also accepts a list of source directories and files to compile + # (equivalent to calling CompileTask#from on the task), and a block for any # post-compilation work. # - # For more information, see Java::CompileTask. - # - # The compile task will pick all the source files in the #java_src_dir directory, - # and unless specified, compile into the #java_target_dir directory. It will pick + # The compile task will pick all the source files in the src/main/java directory, + # and unless specified, compile them into the target/classes directory. It will pick # the default values for compiler options from the parent project's compile task. # # For example: - # compile.options.source = "1.5" - # compile("src").with("log4j:log4j:jar:1.2") - # compile { backport(compile.target) } + # # Force target compatibility. + # compile.options.source = "1.6" + # # Include Apt-generated source files. + # compile.from apt + # # Include Log4J and the api sub-project artifacts. + # compile.with "log4j:log4j:jar:1.2", project("api") + # # Run the OpenJPA bytecode enhancer after compilation. + # compile { open_jpa_enhance } + # + # For more information, see Java::CompileTask. def compile(*sources, &block) task("compile").from(sources).enhance &block end - # The resources task executes after compilation, and will copy resources files + # :call-seq: + # resources(*prereqs) => ResourcesTask + # resources(*prereqs) { |task| .. } => ResourcesTask + # + # The resources task is executed by the compile task to copy resources files # from the resource directory into the target directory. # # This method returns the project's resources task. It also accepts a list of # prerequisites and a block, used to enhance the resources task. # - # By default the resources task copies files from the #resources_dir into the - # same target directory as the #compile task. + # By default the resources task copies files from the src/main/resources into the + # same target directory as the #compile task. It does so using a filter that you + # can access by calling resources.filter (see Buildr::Filter). # - # For more information, see Filter. - def resources(*tasks, &block) - task("resources").enhance tasks, &block + # For example: + # resources.filter.include "config.xml" + # resources.filter.using "Copyright"=>"Acme Inc, 2007" + def resources(*prereqs, &block) + task("resources").enhance prereqs, &block end end Project.on_define do |project| prepare = task("prepare") # Resources task is a filter. - resources = FilterTask.define_task("resources") + resources = Java::ResourcesTask.define_task("resources") + project.path_to("src/main/resources").tap { |dir| resources.filter.include project.path_to(dir, "*") if File.exist?(dir) } # Compile task requires prepare and performs resources, if anything compiled. - compile = Java::CompileTask.define_task("compile"=>prepare) { |task| resources.invoke } + compile = Java::CompileTask.define_task("compile"=>[prepare, resources]) { |task| project.resources.invoke } + project.path_to("src/main/java").tap { |dir| compile.from dir if File.exist?(dir) } + compile.into project.path_to("target/classes") + resources.filter.into project.compile.target project.recursive_task("compile") + project.clean { verbose(false) { rm_rf project.path_to("target") } } project.enhance do |project| - # For convenience, have the prepare task generate the target directory. - #directory project.path_to(:target_dir) - #project.prepare.prerequisites.unshift project.path_to(:target_dir) - # Use the source directory if exists, and set the target directory is not already set. - project.compile.from project.path_to(:java_src_dir) if File.exist?(project.path_to(:java_src_dir)) - project.compile.into project.path_to(:java_target_dir) unless project.compile.target - # Do the same for resources. - project.resources.include project.path_to(:resources_dir, "*") if File.exists?(project.path_to(:resources_dir)) - project.resources.into project.compile.target unless project.resources.target - # Now we know what to build/clean. project.build project.compile.target - project.clean { verbose(false) { rm_rf project.path_to(:target_dir) } } end - end end