buildr 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,30 +7,25 @@ module Buildr
7
7
  #
8
8
  # Supported options are:
9
9
  # - warnings -- Generate warnings if true (opposite of -nowarn).
10
- # - verbose -- Output messages about what the compiler is doing.
11
- # - deprecation -- Output source locations where deprecated APIs
12
- # are used.
10
+ # - deprecation -- Output source locations where deprecated APIs are used.
13
11
  # - source -- Source compatibility with specified release.
14
12
  # - target -- Class file compatibility with specified release.
15
- # - lint -- Value to pass to xlint argument. Use true to enable
16
- # default lint options, or pass a specific setting as string
17
- # or array of strings.
13
+ # - lint -- Value to pass to xlint argument. Use true to enable default lint
14
+ # options, or pass a specific setting as string or array of strings.
18
15
  # - debug -- Generate debugging info.
19
16
  # - other -- Array of options to pass to the Java compiler as is.
20
17
  #
21
18
  # For example:
22
- # options.verbose = true
19
+ # options.warnings = true
23
20
  # options.source = options.target = "1.6"
24
21
  class Options
25
22
 
26
23
  include Attributes
27
24
 
28
- OPTIONS = [:warnings, :verbose, :deprecation, :source, :target, :lint, :debug, :other]
25
+ OPTIONS = [:warnings, :deprecation, :source, :target, :lint, :debug, :other]
29
26
 
30
27
  # Generate warnings (opposite of -nowarn).
31
28
  inherited_attr :warnings
32
- # Output messages about what the compiler is doing.
33
- inherited_attr :verbose
34
29
  # Output source locations where deprecated APIs are used.
35
30
  inherited_attr :deprecation
36
31
  # Provide source compatibility with specified release.
@@ -49,6 +44,7 @@ module Buildr
49
44
  @parent = parent
50
45
  end
51
46
 
47
+ # :nodoc:
52
48
  attr_reader :parent
53
49
 
54
50
  def clear()
@@ -62,12 +58,12 @@ module Buildr
62
58
  # Returns Javac command line arguments from the set of options.
63
59
  def javac_args()
64
60
  args = []
65
- args << "-nowarn" unless warnings && Rake.application.options.trace
66
- args << "-verbose" if verbose
61
+ args << "-nowarn" unless warnings && verbose
62
+ args << "-verbose" if Rake.application.options.trace
67
63
  args << "-g" if debug
68
64
  args << "-deprecation" if deprecation
69
- args << ["-source", source.to_s] if source
70
- args << ["-target", target.to_s] if target
65
+ args << "-source" << source.to_s if source
66
+ args << "-target" << target.to_s if target
71
67
  case lint
72
68
  when Array
73
69
  args << "-Xlint:#{lint.join(',')}"
@@ -76,166 +72,130 @@ module Buildr
76
72
  when true
77
73
  args << "-Xlint"
78
74
  end
79
- args << other if other
75
+ args.concat(other.to_a) if other
80
76
  args
81
77
  end
82
78
 
83
79
  end
84
80
 
85
- # The target directory for the generated class files.
86
- attr_accessor :target
87
81
 
88
82
  def initialize(*args)
89
83
  super
90
- if name[":"] # Only if in namespace
91
- parent = Rake::Task["^compile"]
92
- if parent && parent.respond_to?(:options)
93
- @options = Options.new(parent.options)
94
- end
84
+ parent = Rake::Task["^compile"] if name[":"] # Only if in namespace
85
+ if parent && parent.respond_to?(:options)
86
+ @options = Options.new(parent.options)
87
+ else
88
+ @options = Options.new
95
89
  end
90
+ @sources = []
91
+ @classpath = []
96
92
 
97
93
  enhance do |task|
98
- # Do we have any files to compile?
99
- if target && files.empty?
100
- puts "All source files are up to date" if Rake.application.options.trace && !sources.empty?
101
- elsif target
102
- mkpath target, :verbose=>false
103
- Java.javac files, :sourcepath=>sourcepath, :classpath=>real_classpath,
104
- :output=>target, :javac_args=>options.javac_args, :name=>task.name
105
- # By touching the target we let other tasks know we did something,
106
- # and also prevent recompiling again for classpath dependencies.
107
- touch target, :verbose=>false
108
- end
94
+ mkpath target, :verbose=>false
95
+ Java.javac source_files.keys, :sourcepath=>sources.map(&:to_s).select { |source| File.directory?(source) }.uniq,
96
+ :classpath=>classpath, :output=>target, :javac_args=>options.javac_args, :name=>task.name
97
+ # By touching the target we let other tasks know we did something,
98
+ # and also prevent recompiling again for classpath dependencies.
99
+ touch target, :verbose=>false
109
100
  end
110
101
  end
111
102
 
112
- # An array of source directories and files.
113
- def sources()
114
- @sources ||= []
115
- end
103
+ # Source directories and files to compile.
104
+ attr_accessor :sources
116
105
 
117
- def sources=(paths)
118
- @sources = paths.flatten
106
+ # Adds source directories and files to compile, and returns self.
107
+ #
108
+ # For example:
109
+ # compile.from("src/java").into("classes").with("module1.jar")
110
+ def from(*files)
111
+ @sources |= files.flatten
112
+ self
119
113
  end
120
114
 
121
- # Array of classpath dependencies: files, file tasks, artifacts specs.
122
- def classpath()
123
- @classpath ||= []
124
- end
115
+ # Classpath dependencies.
116
+ attr_accessor :classpath
125
117
 
126
- def classpath=(paths)
127
- @classpath = paths.flatten
118
+ # Adds files and artifacts as classpath dependencies, and returns self.
119
+ #
120
+ # Calls #artifacts on the arguments, so you can pass artifact specifications,
121
+ # tasks, projects, etc. Use this rather than setting the classpath directly.
122
+ #
123
+ # For example:
124
+ # compile.with("module1.jar", "log4j:log4j:jar:1.0", project("foo"))
125
+ def with(*files)
126
+ @classpath |= artifacts(files.flatten).uniq
127
+ self
128
128
  end
129
129
 
130
- # Returns the compiler options.
131
- def options()
132
- @options ||= Options.new
133
- end
130
+ # The target directory for the generated class files.
131
+ attr_reader :target
134
132
 
135
- # Sets the compile options based on a hash of values, or reset to
136
- # the default by passing nil.
137
- def options=(arg)
138
- case arg
139
- when Options
140
- @options = arg
141
- when Hash
142
- options.clear
143
- arg.each { |k,v| options.send "#{k}=", v }
144
- when nil
145
- options.clear
146
- else
147
- raise ArgumentError, "Expecting Options, hash or nil (to reset)"
133
+ # Sets the target directory and returns self. This will also set the compile task
134
+ # as a prerequisite to a file task on the target directory.
135
+ #
136
+ # For example:
137
+ # compile(src_dir).into(target_dir).with(artifacts)
138
+ def into(dir)
139
+ dir = File.expand_path(dir)
140
+ unless @target == dir
141
+ @target = dir
142
+ file(dir).enhance [self]
148
143
  end
149
- @options
150
- end
151
-
152
- # Sets the target directory and returns self. For example:
153
- # compile(src_dir).to(target_dir).with(artifacts)
154
- def to(dir)
155
- self.target = dir
156
144
  self
157
145
  end
158
146
 
159
- # Adds files and artifacts to the classpath and returns self.
160
- # For example:
161
- # compile(src_dir).to(target_dir).with(artifact, file, task)
162
- def with(*args)
163
- self.classpath |= args.flatten
164
- self
165
- end
147
+ # Returns the compiler options.
148
+ attr_reader :options
166
149
 
167
- # Sets the compiler options and returns self. For example:
168
- # compile(src_dir).using(:warnings=>true, :verbose=>true)
150
+ # Sets the compiler options from a hash and returns self.
151
+ #
152
+ # For example:
153
+ # compile.using(:warnings=>true, :source=>"1.5")
169
154
  def using(hash)
170
- self.options = hash
155
+ hash.each { |k, v| options.send "#{k}=", v }
171
156
  self
172
157
  end
173
158
 
174
- # Returns true if any classes were compiled.
175
- def compiled?()
176
- @files && !@files.empty?
177
- end
178
-
179
159
  def timestamp()
180
- File.exist?(target) ? File.stat(target).mtime : Rake::EARLY
160
+ # If we compiled successfully, then the target directory reflects that.
161
+ # If we didn't, see needed?
162
+ @timestamp ||= (File.exist?(target) ? File.stat(target).mtime : Rake::EARLY)
181
163
  end
182
164
 
183
- protected
165
+ def needed?()
166
+ return false if source_files.empty?
167
+ return true unless File.exist?(target)
168
+ return true if source_files.any? { |j, c| !File.exist?(c) || File.stat(j).mtime > File.stat(c).mtime }
169
+ oldest = source_files.map { |j, c| File.stat(c).mtime }.min
170
+ return classpath.any? { |path| application[path].timestamp > oldest }
171
+ end
184
172
 
185
- # Returns the real classpath. Uses the values of #classpath, but resolves
186
- # artifact specifications, projects and other conveniences, executes tasks
187
- # (see #sanitize) and returns a compact array of unique file names.
188
- def real_classpath()
189
- @real_classpath ||= sanitize(artifacts(classpath))
173
+ def prerequisites()
174
+ super + classpath + sources
190
175
  end
191
176
 
192
- # Return the sourcepath, essentialy compact array of directory and file names,
193
- # as set by the user, but after invoking dependencies (see #sanitize).
194
- def sourcepath()
195
- @real_sources ||= sanitize(sources)
196
- @real_sources.select { |source| File.directory?(source) }
177
+ def invoke_prerequisites()
178
+ prerequisites.each { |n| application[n, @scope].invoke }
197
179
  end
198
180
 
181
+ protected
182
+
199
183
  # Returns the files to compile. This list is derived from the list of sources,
200
184
  # expanding directories into files, and includes only source files that are
201
185
  # newer than the corresponding class file. Includes all files if one or more
202
186
  # classpath dependency has been updated.
203
- def files()
204
- unless @files
205
- @real_sources ||= sanitize(sources)
206
- # Compile all files if we compiled nothing, or one of the classpath
207
- # dependencies is newer than the compiled classes.
208
- all = !File.exist?(target) ||
209
- File.stat(target).mtime < (real_classpath.collect{ |file| File.stat(file).mtime } + [ Rake::EARLY ]).max
210
-
211
- if all # Do not restrict to changed files
212
- @files = @real_sources.collect do |source|
213
- File.directory?(source) ? Dir[File.join(source, "**", "*.java")] : source
214
- end.flatten
187
+ def source_files()
188
+ @source_files ||= @sources.map(&:to_s).inject({}) do |map, source|
189
+ raise "Compile task #{name} has source files, but no target directory" unless target
190
+ if File.directory?(source)
191
+ base = Pathname.new(source)
192
+ FileList[File.join(source, "**", "*.java")].
193
+ each { |file| map[file] = File.join(target, Pathname.new(file).relative_path_from(base).to_s.ext(".class")) }
215
194
  else
216
- # Only changed files.
217
- @files = @real_sources.collect do |source|
218
- if File.directory?(source)
219
- Dir[File.join(source, "**", "*.java")].select do |java|
220
- klass = java.sub(source, target).ext(".class")
221
- !File.exist?(klass) || File.stat(java).mtime > File.stat(klass).mtime
222
- end
223
- else
224
- source
225
- end
226
- end.flatten
195
+ map[source] = File.join(target, File.basename(source).ext(".class"))
227
196
  end
197
+ map
228
198
  end
229
- @files
230
- end
231
-
232
- def sanitize(paths)
233
- # Flatten to handle nested arrays often used with dependencies.
234
- # Invoke all tasks, treated as prerequisites (e.g. download artifacts).
235
- # Return task name or file name, ignoring nils and duplicates.
236
- paths.flatten.each { |path| path.invoke if path.respond_to?(:invoke) }.
237
- collect { |path| path.respond_to?(:name) ? path.name : path.to_s }.
238
- compact.uniq
239
199
  end
240
200
 
241
201
  end
@@ -243,26 +203,10 @@ module Buildr
243
203
  end
244
204
 
245
205
 
246
- # Create and return a compiler task. The task is name "compile" in the current
247
- # namespace. Method arguments are passed as sources to compile, and options are
248
- # inherited from any compile task in a parent namespace.
249
- #
250
- # For example:
251
- # compile("src").to("classes").with(artifact, file, task)
252
- def self.compile(*sources)
253
- returning(Java::CompileTask.define_task("compile")) do |task|
254
- task.sources |= sources
255
- end
256
- end
257
-
258
- def compile(*sources)
259
- Buildr.compile(*sources)
260
- end
261
-
262
- # Global task compiles all projects.
206
+ # Local task to execute the compile task of the current project.
207
+ # This task is not itself a compile task.
263
208
  desc "Compile all projects"
264
- LocalDirectoryTask.define_task "compile"
265
-
209
+ Project.local_task(task("compile"))
266
210
 
267
211
  class Project
268
212
 
@@ -277,43 +221,77 @@ module Buildr
277
221
  # The Java target directory. The default is <target_dir>/classes.
278
222
  inherited_attr :java_target_dir do File.join(target_dir, "classes") end
279
223
 
280
- def resources(*tasks, &block)
281
- returning(@resources_task ||= Filter.define_task("resources")) do |task|
282
- task.enhance tasks, &block
283
- end
224
+ # The prepare task executes before the #compile task. Use it for pre-compilation
225
+ # tasks, e.g. generating source code.
226
+ #
227
+ # This method returns the project's prepare task. It also accepts a list of
228
+ # prerequisites and a block, used to enhance the prepare task.
229
+ #
230
+ # By default the prepare task will only generate the #target_dir directory.
231
+ #
232
+ # For example:
233
+ # prepare "src/generated"
234
+ # prepare { javacc.run }
235
+ def prepare(*tasks, &block)
236
+ task("prepare").enhance tasks, &block
284
237
  end
285
238
 
239
+ # The compile task does what its name suggests. This method returns the project's
240
+ # compile task. It also accepts a list of source directories and files to compile
241
+ # (equivalent to calling Java::CompileTask#from on the task), and a block for any
242
+ # post-compilation work.
243
+ #
244
+ # For more information, see Java::CompileTask.
245
+ #
246
+ # The compile task will pick all the source files in the #java_src_dir directory,
247
+ # and unless specified, compile into the #java_target_dir directory. It will pick
248
+ # the default values for compiler options from the parent project's compile task.
249
+ #
250
+ # For example:
251
+ # compile.options.source = "1.5"
252
+ # compile("src").with("log4j:log4j:jar:1.2")
253
+ # compile { backport(compile.target) }
286
254
  def compile(*sources, &block)
287
- returning(@compile_task ||= Java::CompileTask.define_task("compile")) do |task|
288
- task.sources |= sources
289
- task.enhance &block if block
290
- end
255
+ task("compile").from(sources).enhance &block
291
256
  end
292
257
 
293
- def prepare(*tasks, &block)
294
- returning(@prepare_task ||= task("prepare")) do |task|
295
- task.enhance tasks, &block
296
- end
258
+ # The resources task executes after compilation, and will copy resources files
259
+ # from the resource directory into the target directory.
260
+ #
261
+ # This method returns the project's resources task. It also accepts a list of
262
+ # prerequisites and a block, used to enhance the resources task.
263
+ #
264
+ # By default the resources task copies files from the #resources_dir into the
265
+ # same target directory as the #compile task.
266
+ #
267
+ # For more information, see Filter.
268
+ def resources(*tasks, &block)
269
+ task("resources").enhance tasks, &block
297
270
  end
298
271
 
299
272
  end
300
273
 
301
-
302
274
  Project.on_define do |project|
303
- # Prepare is prerequisite for compile, resources follows compile.
304
- project.compile.enhance([project.prepare]) { |task| project.resources.invoke }
275
+ prepare = task("prepare")
276
+ # Resources task is a filter.
277
+ resources = FilterTask.define_task("resources")
278
+ # Compile task requires prepare and performs resources, if anything compiled.
279
+ compile = Java::CompileTask.define_task("compile"=>prepare) { |task| resources.invoke }
305
280
  project.recursive_task("compile")
306
- task("build").enhance [ project.compile ]
307
- task("clean") { rm_rf project.path_to(:target_dir), :verbose=>false }
308
-
309
- project.after_block do |project|
310
- # Automagic compilation only if the source directory exists.
311
- project.compile.sources << project.path_to(:java_src_dir) if File.exist?(project.path_to(:java_src_dir))
312
- project.compile.target ||= project.path_to(:java_target_dir)
313
- project.resources.include project.path_to(:resources_dir, "*") if File.exist?(project.path_to(:resources_dir))
314
- project.resources.target ||= project.compile.target
315
- file(project.compile.target).enhance [ project.compile ]
316
- file(project.resources.target).enhance [ project.resources ]
281
+
282
+ project.enhance do |project|
283
+ # For convenience, have the prepare task generate the target directory.
284
+ #directory project.path_to(:target_dir)
285
+ #project.prepare.prerequisites.unshift project.path_to(:target_dir)
286
+ # Use the source directory if exists, and set the target directory is not already set.
287
+ project.compile.from project.path_to(:java_src_dir) if File.exist?(project.path_to(:java_src_dir))
288
+ project.compile.into project.path_to(:java_target_dir) unless project.compile.target
289
+ # Do the same for resources.
290
+ project.resources.include project.path_to(:resources_dir, "*") if File.exists?(project.path_to(:resources_dir))
291
+ project.resources.into project.compile.target unless project.resources.target
292
+ # Now we know what to build/clean.
293
+ project.build project.compile.target
294
+ project.clean { verbose(false) { rm_rf project.path_to(:target_dir) } }
317
295
  end
318
296
 
319
297
  end
@@ -0,0 +1,79 @@
1
+ require "pathname"
2
+
3
+ module Buildr
4
+
5
+ # Global task "eclipse" generates artifacts for all projects.
6
+ desc "Generate Eclipse artifacts for all projects"
7
+ Project.local_task task("eclipse"=>"artifacts")
8
+
9
+ Project.on_define do |project|
10
+ eclipse = project.recursive_task("eclipse")
11
+
12
+ project.enhance do |project|
13
+
14
+ # We need paths relative to the top project's base directory.
15
+ root_path = lambda { |p| f = lambda { |p| p.parent ? f[p.parent] : p.base_dir } ; f[p] }[project]
16
+ # We're guessing the Rakefile is there, but keep in mind it might not exist
17
+ # (most test cases don't create it).
18
+ sources = []
19
+ sources << File.expand_path(Rake.application.rakefile, root_path) if Rake.application.rakefile
20
+
21
+ # Only for projects that are Eclipse packagable.
22
+ if project.packages.detect { |pkg| pkg.type =~ /(jar)|(war)|(rar)|(mar)|(aar)/ }
23
+ eclipse.enhance [ file(project.path_to(".classpath")), file(project.path_to(".project")) ]
24
+
25
+ # The only thing we need to look for is a change in the Rakefile.
26
+ file(project.path_to(".classpath")=>sources) do |task|
27
+ puts "Writing #{task.name}" if verbose
28
+
29
+ # Find a path relative to the project's root directory.
30
+ relative = lambda { |path| Pathname.new(path).relative_path_from(Pathname.new(project.path_to)).to_s }
31
+
32
+ File.open(task.name, "w") do |file|
33
+ xml = Builder::XmlMarkup.new(:target=>file, :indent=>2)
34
+ xml.classpath do
35
+ # Internal: projects that create artifacts we find on the classpath.
36
+ # External: other artifacts, typically from the local repository.
37
+ internal, external = project.compile.classpath.map(&:to_s).
38
+ map { |path| projects.detect { |prj| prj.packages.detect { |pkg| pkg.to_s == path } } || path }.
39
+ partition { |path| path.is_a?(Project) }
40
+ # Collect all the classpaths, key is the kind, value is
41
+ # string or array (or anything responding to to_a).
42
+ { :src => project.compile.sources.map { |src| relative[src] },
43
+ :output => relative[project.path_to(:java_target_dir)],
44
+ :con => "org.eclipse.jdt.launching.JRE_CONTAINER",
45
+ :lib => external.map(&:to_s)
46
+ }.each do |kind, paths|
47
+ paths.to_a.each { |path| xml.classpathentry :kind=>kind, :path=>path }
48
+ end
49
+ internal.map(&:name).uniq.each do |prj_name|
50
+ xml.classpathentry :kind=>"src", :combineaccessrules=>"false", :path=>"/#{prj_name}"
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ # The only thing we need to look for is a change in the Rakefile.
57
+ file(project.path_to(".project")=>sources) do |task|
58
+ puts "Writing #{task.name}" if verbose
59
+ File.open(task.name, "w") do |file|
60
+ xml = Builder::XmlMarkup.new(:target=>file, :indent=>2)
61
+ xml.projectDescription do
62
+ xml.name project.name
63
+ xml.projects
64
+ xml.buildSpec do
65
+ xml.buildCommand do
66
+ xml.name "org.eclipse.jdt.core.javabuilder"
67
+ end
68
+ end
69
+ xml.natures do
70
+ xml.nature "org.eclipse.jdt.core.javanature"
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ end # module Buildr