buildr 0.16.0 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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