lib/java/compile.rb in buildr-0.16.0 vs lib/java/compile.rb in buildr-0.18.0
- old
+ new
@@ -5,34 +5,29 @@
# Compiler options, accessible from Compiler#options.
#
# Supported options are:
# - warnings -- Generate warnings if true (opposite of -nowarn).
- # - verbose -- Output messages about what the compiler is doing.
- # - deprecation -- Output source locations where deprecated APIs
- # are used.
+ # - deprecation -- Output source locations where deprecated APIs are used.
# - source -- Source compatibility with specified release.
# - target -- Class file compatibility with specified release.
- # - lint -- Value to pass to xlint argument. Use true to enable
- # default lint options, or pass a specific setting as string
- # or array of strings.
+ # - lint -- Value to pass to xlint argument. Use true to enable default lint
+ # 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.verbose = true
+ # options.warnings = true
# options.source = options.target = "1.6"
class Options
include Attributes
- OPTIONS = [:warnings, :verbose, :deprecation, :source, :target, :lint, :debug, :other]
+ OPTIONS = [:warnings, :deprecation, :source, :target, :lint, :debug, :other]
# Generate warnings (opposite of -nowarn).
inherited_attr :warnings
- # Output messages about what the compiler is doing.
- inherited_attr :verbose
# Output source locations where deprecated APIs are used.
inherited_attr :deprecation
# Provide source compatibility with specified release.
inherited_attr :source
# Generate class files for specific VM version.
@@ -47,10 +42,11 @@
def initialize(parent = nil)
@parent = parent
end
+ # :nodoc:
attr_reader :parent
def clear()
OPTIONS.each { |name| send "#{name}=", nil }
end
@@ -60,212 +56,160 @@
end
# Returns Javac command line arguments from the set of options.
def javac_args()
args = []
- args << "-nowarn" unless warnings && Rake.application.options.trace
- args << "-verbose" if verbose
+ args << "-nowarn" unless warnings && verbose
+ args << "-verbose" if Rake.application.options.trace
args << "-g" if debug
args << "-deprecation" if deprecation
- args << ["-source", source.to_s] if source
- args << ["-target", target.to_s] if target
+ args << "-source" << source.to_s if source
+ args << "-target" << target.to_s if target
case lint
when Array
args << "-Xlint:#{lint.join(',')}"
when String
args << "-Xlint:#{lint}"
when true
args << "-Xlint"
end
- args << other if other
+ args.concat(other.to_a) if other
args
end
end
- # The target directory for the generated class files.
- attr_accessor :target
def initialize(*args)
super
- if name[":"] # Only if in namespace
- parent = Rake::Task["^compile"]
- if parent && parent.respond_to?(:options)
- @options = Options.new(parent.options)
- end
+ parent = Rake::Task["^compile"] if name[":"] # Only if in namespace
+ if parent && parent.respond_to?(:options)
+ @options = Options.new(parent.options)
+ else
+ @options = Options.new
end
+ @sources = []
+ @classpath = []
enhance do |task|
- # Do we have any files to compile?
- if target && files.empty?
- puts "All source files are up to date" if Rake.application.options.trace && !sources.empty?
- elsif target
- mkpath target, :verbose=>false
- Java.javac files, :sourcepath=>sourcepath, :classpath=>real_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
- end
+ mkpath target, :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
end
end
- # An array of source directories and files.
- def sources()
- @sources ||= []
- end
+ # Source directories and files to compile.
+ attr_accessor :sources
- def sources=(paths)
- @sources = paths.flatten
+ # 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
+ self
end
- # Array of classpath dependencies: files, file tasks, artifacts specs.
- def classpath()
- @classpath ||= []
- end
+ # Classpath dependencies.
+ attr_accessor :classpath
- def classpath=(paths)
- @classpath = paths.flatten
+ # 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
+ self
end
- # Returns the compiler options.
- def options()
- @options ||= Options.new
- end
+ # The target directory for the generated class files.
+ attr_reader :target
- # Sets the compile options based on a hash of values, or reset to
- # the default by passing nil.
- def options=(arg)
- case arg
- when Options
- @options = arg
- when Hash
- options.clear
- arg.each { |k,v| options.send "#{k}=", v }
- when nil
- options.clear
- else
- raise ArgumentError, "Expecting Options, hash or nil (to reset)"
+ # 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
- @options
- end
-
- # Sets the target directory and returns self. For example:
- # compile(src_dir).to(target_dir).with(artifacts)
- def to(dir)
- self.target = dir
self
end
- # Adds files and artifacts to the classpath and returns self.
- # For example:
- # compile(src_dir).to(target_dir).with(artifact, file, task)
- def with(*args)
- self.classpath |= args.flatten
- self
- end
+ # Returns the compiler options.
+ attr_reader :options
- # Sets the compiler options and returns self. For example:
- # compile(src_dir).using(:warnings=>true, :verbose=>true)
+ # Sets the compiler options from a hash and returns self.
+ #
+ # For example:
+ # compile.using(:warnings=>true, :source=>"1.5")
def using(hash)
- self.options = hash
+ hash.each { |k, v| options.send "#{k}=", v }
self
end
- # Returns true if any classes were compiled.
- def compiled?()
- @files && !@files.empty?
+ def timestamp()
+ # 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)
end
- def timestamp()
- File.exist?(target) ? File.stat(target).mtime : Rake::EARLY
+ def needed?()
+ return false if source_files.empty?
+ return true unless File.exist?(target)
+ 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
- protected
-
- # Returns the real classpath. Uses the values of #classpath, but resolves
- # artifact specifications, projects and other conveniences, executes tasks
- # (see #sanitize) and returns a compact array of unique file names.
- def real_classpath()
- @real_classpath ||= sanitize(artifacts(classpath))
+ def prerequisites()
+ super + classpath + sources
end
- # Return the sourcepath, essentialy compact array of directory and file names,
- # as set by the user, but after invoking dependencies (see #sanitize).
- def sourcepath()
- @real_sources ||= sanitize(sources)
- @real_sources.select { |source| File.directory?(source) }
+ def invoke_prerequisites()
+ 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 files()
- unless @files
- @real_sources ||= sanitize(sources)
- # Compile all files if we compiled nothing, or one of the classpath
- # dependencies is newer than the compiled classes.
- all = !File.exist?(target) ||
- File.stat(target).mtime < (real_classpath.collect{ |file| File.stat(file).mtime } + [ Rake::EARLY ]).max
-
- if all # Do not restrict to changed files
- @files = @real_sources.collect do |source|
- File.directory?(source) ? Dir[File.join(source, "**", "*.java")] : source
- end.flatten
+ 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
+ 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")) }
else
- # Only changed files.
- @files = @real_sources.collect do |source|
- if File.directory?(source)
- Dir[File.join(source, "**", "*.java")].select do |java|
- klass = java.sub(source, target).ext(".class")
- !File.exist?(klass) || File.stat(java).mtime > File.stat(klass).mtime
- end
- else
- source
- end
- end.flatten
+ map[source] = File.join(target, File.basename(source).ext(".class"))
end
+ map
end
- @files
end
- def sanitize(paths)
- # Flatten to handle nested arrays often used with dependencies.
- # Invoke all tasks, treated as prerequisites (e.g. download artifacts).
- # Return task name or file name, ignoring nils and duplicates.
- paths.flatten.each { |path| path.invoke if path.respond_to?(:invoke) }.
- collect { |path| path.respond_to?(:name) ? path.name : path.to_s }.
- compact.uniq
- end
-
end
end
- # Create and return a compiler task. The task is name "compile" in the current
- # namespace. Method arguments are passed as sources to compile, and options are
- # inherited from any compile task in a parent namespace.
- #
- # For example:
- # compile("src").to("classes").with(artifact, file, task)
- def self.compile(*sources)
- returning(Java::CompileTask.define_task("compile")) do |task|
- task.sources |= sources
- end
- end
-
- def compile(*sources)
- Buildr.compile(*sources)
- end
-
- # Global task compiles all projects.
+ # Local task to execute the compile task of the current project.
+ # This task is not itself a compile task.
desc "Compile all projects"
- LocalDirectoryTask.define_task "compile"
+ Project.local_task(task("compile"))
-
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.
@@ -275,46 +219,80 @@
# 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
- def resources(*tasks, &block)
- returning(@resources_task ||= Filter.define_task("resources")) do |task|
- task.enhance tasks, &block
- end
+ # 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
end
+ # 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
+ # 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 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) }
def compile(*sources, &block)
- returning(@compile_task ||= Java::CompileTask.define_task("compile")) do |task|
- task.sources |= sources
- task.enhance &block if block
- end
+ task("compile").from(sources).enhance &block
end
- def prepare(*tasks, &block)
- returning(@prepare_task ||= task("prepare")) do |task|
- task.enhance tasks, &block
- end
+ # The resources task executes after compilation, and will 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.
+ #
+ # For more information, see Filter.
+ def resources(*tasks, &block)
+ task("resources").enhance tasks, &block
end
end
-
Project.on_define do |project|
- # Prepare is prerequisite for compile, resources follows compile.
- project.compile.enhance([project.prepare]) { |task| project.resources.invoke }
+ prepare = task("prepare")
+ # Resources task is a filter.
+ resources = FilterTask.define_task("resources")
+ # Compile task requires prepare and performs resources, if anything compiled.
+ compile = Java::CompileTask.define_task("compile"=>prepare) { |task| resources.invoke }
project.recursive_task("compile")
- task("build").enhance [ project.compile ]
- task("clean") { rm_rf project.path_to(:target_dir), :verbose=>false }
- project.after_block do |project|
- # Automagic compilation only if the source directory exists.
- project.compile.sources << project.path_to(:java_src_dir) if File.exist?(project.path_to(:java_src_dir))
- project.compile.target ||= project.path_to(:java_target_dir)
- project.resources.include project.path_to(:resources_dir, "*") if File.exist?(project.path_to(:resources_dir))
- project.resources.target ||= project.compile.target
- file(project.compile.target).enhance [ project.compile ]
- file(project.resources.target).enhance [ project.resources ]
+ 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