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