realityforge-buildr 1.5.9

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.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE +176 -0
  4. data/NOTICE +26 -0
  5. data/README.md +3 -0
  6. data/Rakefile +50 -0
  7. data/addon/buildr/checkstyle-report.xsl +104 -0
  8. data/addon/buildr/checkstyle.rb +254 -0
  9. data/addon/buildr/git_auto_version.rb +36 -0
  10. data/addon/buildr/gpg.rb +90 -0
  11. data/addon/buildr/gwt.rb +413 -0
  12. data/addon/buildr/jacoco.rb +161 -0
  13. data/addon/buildr/pmd.rb +185 -0
  14. data/addon/buildr/single_intermediate_layout.rb +71 -0
  15. data/addon/buildr/spotbugs.rb +265 -0
  16. data/addon/buildr/top_level_generate_dir.rb +37 -0
  17. data/addon/buildr/wsgen.rb +192 -0
  18. data/bin/buildr +20 -0
  19. data/buildr.gemspec +61 -0
  20. data/lib/buildr.rb +86 -0
  21. data/lib/buildr/core/application.rb +705 -0
  22. data/lib/buildr/core/assets.rb +96 -0
  23. data/lib/buildr/core/build.rb +587 -0
  24. data/lib/buildr/core/common.rb +167 -0
  25. data/lib/buildr/core/compile.rb +599 -0
  26. data/lib/buildr/core/console.rb +124 -0
  27. data/lib/buildr/core/doc.rb +275 -0
  28. data/lib/buildr/core/environment.rb +128 -0
  29. data/lib/buildr/core/filter.rb +405 -0
  30. data/lib/buildr/core/help.rb +114 -0
  31. data/lib/buildr/core/progressbar.rb +161 -0
  32. data/lib/buildr/core/project.rb +994 -0
  33. data/lib/buildr/core/test.rb +776 -0
  34. data/lib/buildr/core/transports.rb +456 -0
  35. data/lib/buildr/core/util.rb +77 -0
  36. data/lib/buildr/ide/idea.rb +1664 -0
  37. data/lib/buildr/java/commands.rb +230 -0
  38. data/lib/buildr/java/compiler.rb +85 -0
  39. data/lib/buildr/java/custom_pom.rb +300 -0
  40. data/lib/buildr/java/doc.rb +62 -0
  41. data/lib/buildr/java/packaging.rb +393 -0
  42. data/lib/buildr/java/pom.rb +191 -0
  43. data/lib/buildr/java/test_result.rb +54 -0
  44. data/lib/buildr/java/tests.rb +111 -0
  45. data/lib/buildr/packaging/archive.rb +586 -0
  46. data/lib/buildr/packaging/artifact.rb +1113 -0
  47. data/lib/buildr/packaging/artifact_namespace.rb +1010 -0
  48. data/lib/buildr/packaging/artifact_search.rb +138 -0
  49. data/lib/buildr/packaging/package.rb +237 -0
  50. data/lib/buildr/packaging/version_requirement.rb +189 -0
  51. data/lib/buildr/packaging/zip.rb +189 -0
  52. data/lib/buildr/packaging/ziptask.rb +387 -0
  53. data/lib/buildr/version.rb +18 -0
  54. data/rakelib/release.rake +99 -0
  55. data/spec/addon/checkstyle_spec.rb +58 -0
  56. data/spec/core/application_spec.rb +576 -0
  57. data/spec/core/build_spec.rb +922 -0
  58. data/spec/core/common_spec.rb +670 -0
  59. data/spec/core/compile_spec.rb +656 -0
  60. data/spec/core/console_spec.rb +65 -0
  61. data/spec/core/doc_spec.rb +194 -0
  62. data/spec/core/extension_spec.rb +200 -0
  63. data/spec/core/project_spec.rb +736 -0
  64. data/spec/core/test_spec.rb +1131 -0
  65. data/spec/core/transport_spec.rb +452 -0
  66. data/spec/core/util_spec.rb +154 -0
  67. data/spec/ide/idea_spec.rb +1952 -0
  68. data/spec/java/commands_spec.rb +79 -0
  69. data/spec/java/compiler_spec.rb +274 -0
  70. data/spec/java/custom_pom_spec.rb +165 -0
  71. data/spec/java/doc_spec.rb +55 -0
  72. data/spec/java/packaging_spec.rb +786 -0
  73. data/spec/java/pom_spec.rb +162 -0
  74. data/spec/java/test_coverage_helper.rb +257 -0
  75. data/spec/java/tests_spec.rb +224 -0
  76. data/spec/packaging/archive_spec.rb +686 -0
  77. data/spec/packaging/artifact_namespace_spec.rb +757 -0
  78. data/spec/packaging/artifact_spec.rb +1351 -0
  79. data/spec/packaging/packaging_helper.rb +63 -0
  80. data/spec/packaging/packaging_spec.rb +690 -0
  81. data/spec/sandbox.rb +166 -0
  82. data/spec/spec_helpers.rb +420 -0
  83. data/spec/version_requirement_spec.rb +145 -0
  84. data/spec/xpath_matchers.rb +123 -0
  85. metadata +295 -0
@@ -0,0 +1,161 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with this
3
+ # work for additional information regarding copyright ownership. The ASF
4
+ # licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ #
17
+ class ProgressBar
18
+
19
+ class << self
20
+
21
+ def start(args, &block)
22
+ new(args).start &block
23
+ end
24
+
25
+ def width
26
+ @width ||= Buildr::Console.output_cols || 0
27
+ end
28
+
29
+ end
30
+
31
+ def initialize(args = {})
32
+ @title = args[:title] || ''
33
+ @total = args[:total] || 0
34
+ @mark = args[:mark] || '.'
35
+ @format = args[:format] || default_format
36
+ @output = args[:output] || $stderr unless args[:hidden] || !$stdout.isatty
37
+ clear
38
+ end
39
+
40
+ def start
41
+ @start = @last_time = Time.now
42
+ @count = 0
43
+ @finished = false
44
+ render
45
+ if block_given?
46
+ result = yield(self) if block_given?
47
+ finish
48
+ result
49
+ else
50
+ self
51
+ end
52
+ end
53
+
54
+ def inc(count)
55
+ set @count + count
56
+ end
57
+
58
+ def <<(bytes)
59
+ inc bytes.size
60
+ end
61
+
62
+ def set(count)
63
+ @count = [count, 0].max
64
+ @count = [count, @total].min unless @total == 0
65
+ render if changed?
66
+ end
67
+
68
+ def title
69
+ return @title if ProgressBar.width <= 10
70
+ @title.size > ProgressBar.width / 5 ? (@title[0, ProgressBar.width / 5 - 2] + '..') : @title
71
+ end
72
+
73
+ def count
74
+ human(@count)
75
+ end
76
+
77
+ def total
78
+ human(@total)
79
+ end
80
+
81
+ def percentage
82
+ '%3d%%' % (@total == 0 ? 100 : (@count * 100 / @total))
83
+ end
84
+
85
+ def time
86
+ @finished ? elapsed : eta
87
+ end
88
+
89
+ def eta
90
+ return 'ETA: --:--:--' if @count == 0
91
+ elapsed = Time.now - @start
92
+ eta = elapsed * @total / @count - elapsed
93
+ 'ETA: %s' % duration(eta.ceil)
94
+ end
95
+
96
+ def elapsed
97
+ 'Time: %s' % duration(Time.now - @start)
98
+ end
99
+
100
+ def rate
101
+ '%s/s' % human(@count / (Time.now - @start))
102
+ end
103
+
104
+ def progress(width)
105
+ width -= 2
106
+ marks = @total == 0 ? width : (@count * width / @total)
107
+ "|%-#{width}s|" % (@mark * marks)
108
+ end
109
+
110
+ def human(bytes)
111
+ magnitude = (0..3).find { |i| bytes < (1024 << i * 10) } || 3
112
+ return '%dB' % bytes if magnitude == 0
113
+ return '%.1f%s' % [ bytes.to_f / (1 << magnitude * 10), [nil, 'KB', 'MB', 'GB'][magnitude] ]
114
+ end
115
+
116
+ def duration(seconds)
117
+ '%02d:%02d:%02d' % [seconds / 3600, (seconds / 60) % 60, seconds % 60]
118
+ end
119
+
120
+ def finish
121
+ unless @finished
122
+ @finished = true
123
+ render
124
+ end
125
+ end
126
+
127
+ protected
128
+
129
+ def clear
130
+ return if @output == nil || ProgressBar.width <= 0
131
+ @output.print "\r", " " * (ProgressBar.width - 1), "\r"
132
+ @output.flush
133
+ end
134
+
135
+ def render
136
+ return unless @output
137
+ format, *args = @format
138
+ line = format % args.map { |arg| send(arg) }
139
+ if ProgressBar.width >= line.size
140
+ @output.print line.sub('|--|') { progress(ProgressBar.width - line.size + 3) }
141
+ else
142
+ @output.print line.sub('|--|', '')
143
+ end
144
+ @output.print @finished ? "\n" : "\r"
145
+ @output.flush
146
+ @previous = @count
147
+ @last_time = Time.now
148
+ end
149
+
150
+ def changed?
151
+ return false unless @output && Time.now - @last_time > 0.1
152
+ return human(@count) != human(@previous) if @total == 0
153
+ return true if (@count - @previous) >= @total / 100
154
+ return Time.now - @last_time > 1
155
+ end
156
+
157
+ def default_format
158
+ @total == 0 ? ['%s %8s %s', :title, :count, :elapsed] : ['%s: %s |--| %8s/%s %s', :title, :percentage, :count, :total, :time]
159
+ end
160
+
161
+ end
@@ -0,0 +1,994 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with this
3
+ # work for additional information regarding copyright ownership. The ASF
4
+ # licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ module Buildr #:nodoc:
17
+
18
+ # Symbolic mapping for directory layout. Used for both the default and custom layouts.
19
+ #
20
+ # For example, the default layout maps [:source, :main, :java] to 'src/main/java', and
21
+ # [:target, :main, :classes] to 'target/classes'. You can use this to change the layout
22
+ # of your projects.
23
+ #
24
+ # To map [:source, :main] into the 'sources' directory:
25
+ # my_layout = Layout.new
26
+ # my_layout[:source, :main] = 'sources'
27
+ #
28
+ # define 'foo', :layout=>my_layout do
29
+ # ...
30
+ # end
31
+ #
32
+ # To map [:source, :main, :java] to 'java/main':
33
+ # class MainLast < Layout
34
+ # def expand(*args)
35
+ # if args[0..1] == [:source, :main]
36
+ # super args[2], :main, *args[3,]
37
+ # else
38
+ # super
39
+ # end
40
+ # end
41
+ # end
42
+ #
43
+ # define 'foo', :layout=>MainLast do
44
+ # ...
45
+ # end
46
+ class Layout
47
+
48
+ class << self
49
+
50
+ # Default layout used by new projects.
51
+ attr_accessor :default
52
+
53
+ end
54
+
55
+ def initialize #:nodoc:
56
+ @mapping = {}
57
+ end
58
+
59
+ # Expands list of symbols and path names into a full path, for example:
60
+ # puts default.expand(:source, :main, :java)
61
+ # => "src/main/java"
62
+ def expand(*args)
63
+ args = args.compact.reject { |s| s.to_s.empty? }.map(&:to_sym)
64
+ return '' if args.empty?
65
+ @mapping[args] ||= File.join(*[expand(*args[0..-2]), args.last.to_s].reject(&:empty?)) if args.size > 1
66
+ return @mapping[args] || args.first.to_s
67
+ end
68
+
69
+ # Resolves a list of symbols into a path.
70
+ def [](*args)
71
+ @mapping[args.map(&:to_sym)]
72
+ end
73
+
74
+ # Specifies the path resolved from a list of symbols.
75
+ def []=(*args)
76
+ @mapping[args[0...-1].map(&:to_sym)] = args.last
77
+ end
78
+
79
+ def initialize_copy(copy)
80
+ copy.instance_variable_set :@mapping, @mapping.clone
81
+ end
82
+
83
+ # Default layout has the following properties:
84
+ # * :source maps to the 'src' directory.
85
+ # * Anything under :source maps verbatim (e.g. :source, :main becomes 'src/main')
86
+ # * :target maps to the 'target' directory.
87
+ # * :target, :main maps to the 'target' directory as well.
88
+ # * Anything under :target, :main maps verbatim (e.g. :target, :main, :classes becomes 'target/classes')
89
+ # * Anything else under :target also maps verbatim (e.g. :target, :test becomes 'target/test')
90
+ class Default < Layout
91
+
92
+ def initialize
93
+ super
94
+ self[:source] = 'src'
95
+ self[:target, :main] = 'target'
96
+ end
97
+
98
+ end
99
+
100
+ self.default = Default.new
101
+
102
+ end
103
+
104
+
105
+ # A project definition is where you define all the tasks associated with
106
+ # the project you're building.
107
+ #
108
+ # The project itself will define several life cycle tasks for you. For example,
109
+ # it automatically creates a compile task that will compile all the source files
110
+ # found in src/main/java into target/classes, a test task that will compile source
111
+ # files from src/test/java and run all the tests found there, and a build
112
+ # task to compile and then run the tests.
113
+ #
114
+ # You use the project definition to enhance these tasks, for example, telling the
115
+ # compile task which class path dependencies to use. Or telling the project how
116
+ # to package an artifact, e.g. creating a JAR using <tt>package :jar</tt>.
117
+ #
118
+ # You can also define additional tasks that are executed by project tasks,
119
+ # or invoked from rake.
120
+ #
121
+ # Tasks created by the project are all prefixed with the project name, e.g.
122
+ # the project foo creates the task foo:compile. If foo contains a sub-project bar,
123
+ # the later will define the task foo:bar:compile. Since the compile task is
124
+ # recursive, compiling foo will also compile foo:bar.
125
+ #
126
+ # If you run:
127
+ # buildr compile
128
+ # from the command line, it will execute the compile task of the current project.
129
+ #
130
+ # Projects and sub-projects follow a directory hierarchy. The Buildfile is assumed to
131
+ # reside in the same directory as the top-level project, and each sub-project is
132
+ # contained in a sub-directory in the same name. For example:
133
+ # /home/foo
134
+ # |__ Buildfile
135
+ # |__ src/main/java
136
+ # |__ foo
137
+ # |__ src/main/java
138
+ #
139
+ # The default structure of each project is assumed to be:
140
+ # src
141
+ # |__main
142
+ # | |__java <-- Source files to compile
143
+ # | |__resources <-- Resources to copy
144
+ # | |__webapp <-- For WARs
145
+ # |__test
146
+ # | |__java <-- Source files to compile (tests)
147
+ # | |__resources <-- Resources to copy (tests)
148
+ # |__target <-- Packages created here
149
+ # | |__classes <-- Generated when compiling
150
+ # | |__resources <-- Copied (and filtered) from resources
151
+ # | |__test/classes <-- Generated when compiling tests
152
+ # | |__test/resources <-- Copied (and filtered) from resources
153
+ # |__reports <-- Test, coverage and other reports
154
+ #
155
+ # You can change the project layout by passing a new Layout to the project definition.
156
+ #
157
+ # You can only define a project once using #define. Afterwards, you can obtain the project
158
+ # definition using #project. The order in which you define projects is not important,
159
+ # project definitions are evaluated when you ask for them. Circular dependencies will not
160
+ # work. Rake tasks are only created after the project is evaluated, so if you need to access
161
+ # a task (e.g. compile) use <code>project('foo').compile</code> instead of <code>task('foo:compile')</code>.
162
+ #
163
+ # For example:
164
+ # define 'myapp', :version=>'1.1' do
165
+ #
166
+ # define 'wepapp' do
167
+ # compile.with project('myapp:beans')
168
+ # package :war
169
+ # end
170
+ #
171
+ # define 'beans' do
172
+ # compile.with DEPENDS
173
+ # package :jar
174
+ # end
175
+ # end
176
+ #
177
+ # puts projects.map(&:name)
178
+ # => [ 'myapp', 'myapp:beans', 'myapp:webapp' ]
179
+ # puts project('myapp:webapp').parent.name
180
+ # => 'myapp'
181
+ # puts project('myapp:webapp').compile.classpath.map(&:to_spec)
182
+ # => 'myapp:myapp-beans:jar:1.1'
183
+ class Project < Rake::Task
184
+
185
+ class << self
186
+
187
+ # :call-seq:
188
+ # define(name, properties?) { |project| ... } => project
189
+ #
190
+ # See Buildr#define.
191
+ def define(name, properties, &block) #:nodoc:
192
+ # Make sure a sub-project is only defined within the parent project,
193
+ # to prevent silly mistakes that lead to inconsistencies (e.g.
194
+ # namespaces will be all out of whack).
195
+ Buildr.application.current_scope == name.split(':')[0...-1] or
196
+ raise "You can only define a sub project (#{name}) within the definition of its parent project"
197
+
198
+ @projects ||= {}
199
+ raise "You cannot define the same project (#{name}) more than once" if @projects[name]
200
+ # Projects with names like: compile, test, build are invalid, so we have
201
+ # to make sure the project has not the name of an already defined task
202
+ raise "Invalid project name: #{name.inspect} is already used for a task" if Buildr.application.lookup(name)
203
+
204
+ Project.define_task(name).tap do |project|
205
+ # Define the project to prevent duplicate definition.
206
+ @projects[name] = project
207
+ # Set the project properties first, actions may use them.
208
+ properties.each { |name, value| project.send "#{name}=", value } if properties
209
+ # Setup to call before/after define extension callbacks
210
+ # Don't cache list of extensions, since project may add new extensions.
211
+ project.enhance do |project|
212
+ project.send :call_callbacks, :before_define
213
+ project.enhance do |project|
214
+ project.send :call_callbacks, :after_define
215
+ end
216
+ end
217
+ # Enhance the project using the definition block.
218
+ project.enhance { project.instance_exec project, &block } if block
219
+
220
+ # Mark the project as defined
221
+ project.enhance do |project|
222
+ project.send :define!
223
+ end
224
+
225
+ # Top-level project? Invoke the project definition. Sub-project? We don't invoke
226
+ # the project definition yet (allow project calls to establish order of evaluation),
227
+ # but must do so before the parent project's definition is done.
228
+ project.parent.enhance { project.invoke } if project.parent
229
+ end
230
+ end
231
+
232
+ # :call-seq:
233
+ # project(name) => project
234
+ #
235
+ # See Buildr#project.
236
+ def project(*args, &block) #:nodoc:
237
+ options = args.pop if Hash === args.last
238
+ return define(args.first, options, &block) if block
239
+ rake_check_options options, :scope, :no_invoke if options
240
+ no_invoke = options && options[:no_invoke]
241
+
242
+ raise ArgumentError, 'Only one project name at a time' unless args.size == 1
243
+ @projects ||= {}
244
+ name = args.first.to_s
245
+ # Make sure parent project is evaluated (e.g. if looking for foo:bar, find foo first)
246
+ unless @projects[name]
247
+ parts = name.split(':')
248
+ project(parts.first, options || {}) if parts.size > 1
249
+ end
250
+ if options && options[:scope]
251
+ # We assume parent project is evaluated.
252
+ project = options[:scope].split(':').inject([[]]) { |scopes, scope| scopes << (scopes.last + [scope]) }.
253
+ map { |scope| @projects[(scope + [name]).join(':')] }.
254
+ select { |project| project }.last
255
+ end
256
+ project ||= @projects[name] # Not found in scope.
257
+ raise "No such project #{name}" unless project
258
+ project.invoke unless project.defined? || no_invoke || Buildr.application.current_scope.join(":").to_s == project.name.to_s
259
+ project
260
+ end
261
+
262
+ # :call-seq:
263
+ # projects(*names) => projects
264
+ #
265
+ # See Buildr#projects.
266
+ def projects(*names) #:nodoc:
267
+ options = names.pop if Hash === names.last
268
+ rake_check_options options, :scope, :no_invoke if options
269
+
270
+ no_invoke = options && options[:no_invoke]
271
+
272
+ @projects ||= {}
273
+ names = names.flatten
274
+ if options && options[:scope]
275
+ # We assume parent project is evaluated.
276
+ if names.empty?
277
+ parent = @projects[options[:scope].to_s] or raise "No such project #{options[:scope]}"
278
+ @projects.values.select { |project| project.parent == parent }.each { |project| project.invoke unless no_invoke }.
279
+ map { |project| [project] + projects(:scope => project, :no_invoke => no_invoke) }.flatten.sort_by(&:name)
280
+ else
281
+ names.uniq.map { |name| project(name, :scope => options[:scope], :no_invoke => no_invoke) }
282
+ end
283
+ elsif names.empty?
284
+ # Parent project(s) not evaluated so we don't know all the projects yet.
285
+ @projects.values.each { |project| project.invoke unless no_invoke }
286
+ @projects.keys.map { |name| project(name, :no_invoke => no_invoke) or raise "No such project #{name}" }.sort_by(&:name)
287
+ else
288
+ # Parent project(s) not evaluated, for the sub-projects we may need to find.
289
+ names.map { |name| name.split(':') }.select { |name| name.size > 1 }.map(&:first).uniq.each { |name| project(name, :no_invoke => no_invoke) }
290
+ names.uniq.map { |name| project(name, :no_invoke => no_invoke) or raise "No such project #{name}" }.sort_by(&:name)
291
+ end
292
+ end
293
+
294
+ # :call-seq:
295
+ # clear
296
+ #
297
+ # Discard all project definitions.
298
+ def clear
299
+ @projects.clear if @projects
300
+ end
301
+
302
+ # :call-seq:
303
+ # local_task(name)
304
+ # local_task(name) { |name| ... }
305
+ #
306
+ # Defines a local task with an optional execution message.
307
+ #
308
+ # A local task is a task that executes a task with the same name, defined in the
309
+ # current project, the project's with a base directory that is the same as the
310
+ # current directory.
311
+ #
312
+ # Complicated? Try this:
313
+ # buildr build
314
+ # is the same as:
315
+ # buildr foo:build
316
+ # But:
317
+ # cd bar
318
+ # buildr build
319
+ # is the same as:
320
+ # buildr foo:bar:build
321
+ #
322
+ # The optional block is called with the project name when the task executes
323
+ # and returns a message that, for example "Building project #{name}".
324
+ def local_task(*args, &block)
325
+ task *args do |task, args|
326
+ args = task.arg_names.map {|n| args[n]}
327
+ local_projects do |project|
328
+ info block.call(project.name) if block
329
+ task("#{project.name}:#{task.name}").invoke *args
330
+ end
331
+ end
332
+ end
333
+
334
+ def scope_name(scope, task_name) #:nodoc:
335
+ task_name
336
+ end
337
+
338
+ def local_projects(dir = nil, &block) #:nodoc:
339
+ dir = File.expand_path(dir || Buildr.application.original_dir)
340
+ projects = @projects ? @projects.values : []
341
+ projects = projects.select { |project| project.base_dir == dir }
342
+ if projects.empty? && dir != Dir.pwd && File.dirname(dir) != dir
343
+ local_projects(File.dirname(dir), &block)
344
+ elsif block
345
+ if projects.empty?
346
+ warn "No projects defined for directory #{Buildr.application.original_dir}"
347
+ else
348
+ projects.each { |project| block[project] }
349
+ end
350
+ else
351
+ projects
352
+ end
353
+ end
354
+
355
+ # :call-seq:
356
+ # parent_task(task_name) => task_name or nil
357
+ #
358
+ # Returns a parent task, basically a task in a higher namespace. For example, the parent
359
+ # of 'foo:test:compile' is 'foo:compile' and the parent of 'foo:compile' is 'compile'.
360
+ def parent_task(task_name) #:nodoc:
361
+ namespace = task_name.split(':')
362
+ last_name = namespace.pop
363
+ namespace.pop
364
+ Buildr.application.lookup((namespace + [last_name]).join(':'), []) unless namespace.empty?
365
+ end
366
+
367
+ # :call-seq:
368
+ # project_from_task(task) => project
369
+ #
370
+ # Figure out project associated to this task and return it.
371
+ def project_from_task(task) #:nodoc:
372
+ project = Buildr.application.lookup('rake:' + task.to_s.gsub(/:[^:]*$/, ''))
373
+ project if Project === project
374
+ end
375
+
376
+ # Loaded extension modules.
377
+ def extension_modules #:nodoc:
378
+ @extension_modules ||= []
379
+ end
380
+
381
+ # Extension callbacks that apply to all projects
382
+ def global_callbacks #:nodoc:
383
+ @global_callbacks ||= []
384
+ end
385
+ end
386
+
387
+
388
+ # Project has visibility to everything in the Buildr namespace.
389
+ include Buildr
390
+
391
+ # The project name. For example, 'foo' for the top-level project, and 'foo:bar'
392
+ # for its sub-project.
393
+ attr_reader :name
394
+
395
+ # The parent project if this is a sub-project.
396
+ attr_reader :parent
397
+
398
+ def initialize(*args) #:nodoc:
399
+ super
400
+ split = name.split(':')
401
+ if split.size > 1
402
+ # Get parent project, but do not invoke it's definition to prevent circular
403
+ # dependencies (it's being invoked right now, so calling project will fail).
404
+ @parent = task(split[0...-1].join(':'))
405
+ raise "No parent project #{split[0...-1].join(':')}" unless @parent && Project === parent
406
+ end
407
+ # Inherit all global callbacks
408
+ @callbacks = Project.global_callbacks.dup
409
+ end
410
+
411
+ #
412
+ # Returns the root project for this project.
413
+ #
414
+ # If this project is a subproject it will find the top
415
+ # level project and return it, else it will return itself.
416
+ #
417
+ def root_project
418
+ p = project
419
+ while p.parent
420
+ p = p.parent
421
+ end
422
+ p
423
+ end
424
+
425
+ # :call-seq:
426
+ # base_dir => path
427
+ #
428
+ # Returns the project's base directory.
429
+ #
430
+ # The Buildfile defines top-level project, so it's logical that the top-level project's
431
+ # base directory is the one in which we find the Buildfile. And each sub-project has
432
+ # a base directory that is one level down, with the same name as the sub-project.
433
+ #
434
+ # For example:
435
+ # /home/foo/ <-- base_directory of project 'foo'
436
+ # /home/foo/Buildfile <-- builds 'foo'
437
+ # /home/foo/bar <-- sub-project 'foo:bar'
438
+ def base_dir
439
+ if @base_dir.nil?
440
+ if parent
441
+ # For sub-project, a good default is a directory in the parent's base_dir,
442
+ # using the same name as the project.
443
+ @base_dir = File.expand_path(name.split(':').last, parent.base_dir)
444
+ else
445
+ # For top-level project, a good default is the directory where we found the Buildfile.
446
+ @base_dir = Dir.pwd
447
+ end
448
+ end
449
+ @base_dir
450
+ end
451
+
452
+ # Returns the layout associated with this project.
453
+ def layout
454
+ @layout ||= (parent ? parent.layout : Layout.default).clone
455
+ end
456
+
457
+ # :call-seq:
458
+ # path_to(*names) => path
459
+ #
460
+ # Returns a path from a combination of name, relative to the project's base directory.
461
+ # Essentially, joins all the supplied names and expands the path relative to #base_dir.
462
+ # Symbol arguments are converted to paths based on the layout, so whenever possible stick
463
+ # to these. For example:
464
+ # path_to(:source, :main, :java)
465
+ # => 'src/main/java'
466
+ #
467
+ # Keep in mind that all tasks are defined and executed relative to the Buildfile directory,
468
+ # so you want to use #path_to to get the actual path within the project as a matter of practice.
469
+ #
470
+ # For example:
471
+ # path_to('foo', 'bar')
472
+ # => foo/bar
473
+ # path_to('/tmp')
474
+ # => /tmp
475
+ # path_to(:base_dir, 'foo') # same as path_to('foo")
476
+ # => /home/project1/foo
477
+ def path_to(*names)
478
+ File.expand_path(layout.expand(*names), base_dir)
479
+ end
480
+ alias :_ :path_to
481
+
482
+ # :call-seq:
483
+ # file(path) => Task
484
+ # file(path=>prereqs) => Task
485
+ # file(path) { |task| ... } => Task
486
+ #
487
+ # Creates and returns a new file task in the project. Similar to calling Rake's
488
+ # file method, but the path is expanded relative to the project's base directory,
489
+ # and the task executes in the project's base directory.
490
+ #
491
+ # For example:
492
+ # define 'foo' do
493
+ # define 'bar' do
494
+ # file('src') { ... }
495
+ # end
496
+ # end
497
+ #
498
+ # puts project('foo:bar').file('src').to_s
499
+ # => '/home/foo/bar/src'
500
+ def file(*args, &block)
501
+ task_name, arg_names, deps = Buildr.application.resolve_args(args)
502
+ task = Rake::FileTask.define_task(path_to(task_name))
503
+ task.set_arg_names(arg_names) unless arg_names.empty?
504
+ task.enhance Array(deps), &block
505
+ end
506
+
507
+ # :call-seq:
508
+ # task(name) => Task
509
+ # task(name=>prereqs) => Task
510
+ # task(name) { |task| ... } => Task
511
+ #
512
+ # Creates and returns a new task in the project. Similar to calling Rake's task
513
+ # method, but prefixes the task name with the project name and executes the task
514
+ # in the project's base directory.
515
+ #
516
+ # For example:
517
+ # define 'foo' do
518
+ # task 'doda'
519
+ # end
520
+ #
521
+ # puts project('foo').task('doda').name
522
+ # => 'foo:doda'
523
+ #
524
+ # When called from within the project definition, creates a new task if the task
525
+ # does not already exist. If called from outside the project definition, returns
526
+ # the named task and raises an exception if the task is not defined.
527
+ #
528
+ # As with Rake's task method, calling this method enhances the task with the
529
+ # prerequisites and optional block.
530
+ def task(*args, &block)
531
+ task_name, arg_names, deps = Buildr.application.resolve_args(args)
532
+ if task_name =~ /^:/
533
+ task = Buildr.application.switch_to_namespace [] do
534
+ Rake::Task.define_task(task_name[1..-1])
535
+ end
536
+ elsif Buildr.application.current_scope == name.split(':')
537
+ task = Rake::Task.define_task(task_name)
538
+ else
539
+ unless task = Buildr.application.lookup(task_name, name.split(':'))
540
+ raise "You cannot define a project task outside the project definition, and no task #{name}:#{task_name} defined in the project"
541
+ end
542
+ end
543
+ task.set_arg_names(arg_names) unless arg_names.empty?
544
+ task.enhance Array(deps), &block
545
+ end
546
+
547
+ # :call-seq:
548
+ # recursive_task(name=>prereqs) { |task| ... }
549
+ #
550
+ # Define a recursive task. A recursive task executes itself and the same task
551
+ # in all the sub-projects.
552
+ def recursive_task(*args, &block)
553
+ task_name, arg_names, deps = Buildr.application.resolve_args(args)
554
+ task = Buildr.options.parallel ? multitask(task_name) : task(task_name)
555
+ parent.task(task_name).enhance [task] if parent
556
+ task.set_arg_names(arg_names) unless arg_names.empty?
557
+ task.enhance Array(deps), &block
558
+ end
559
+
560
+ # :call-seq:
561
+ # project(name) => project
562
+ # project => self
563
+ #
564
+ # Same as Buildr#project. This method is called on a project, so a relative name is
565
+ # sufficient to find a sub-project.
566
+ #
567
+ # When called on a project without a name, returns the project itself. You can use that when
568
+ # setting project properties, for example:
569
+ # define 'foo' do
570
+ # project.version = '1.0'
571
+ # end
572
+ def project(*args, &block)
573
+ if Hash === args.last
574
+ options = args.pop
575
+ else
576
+ options = {}
577
+ end
578
+ if args.empty?
579
+ self
580
+ else
581
+ Project.project *(args + [{ :scope=>self.name }.merge(options)]), &block
582
+ end
583
+ end
584
+
585
+ # :call-seq:
586
+ # projects(*names) => projects
587
+ #
588
+ # Same as Buildr#projects. This method is called on a project, so relative names are
589
+ # sufficient to find sub-projects.
590
+ def projects(*args)
591
+ if Hash === args.last
592
+ options = args.pop
593
+ else
594
+ options = {}
595
+ end
596
+ Project.projects *(args + [{ :scope=>self.name }.merge(options)])
597
+ end
598
+
599
+ def inspect #:nodoc:
600
+ %Q{project(#{name.inspect})}
601
+ end
602
+
603
+ def callbacks #:nodoc:
604
+ # global + project_local callbacks for this project
605
+ @callbacks ||= []
606
+ end
607
+
608
+ def calledback #:nodoc:
609
+ # project-local callbacks that have been called
610
+ @calledback ||= {}
611
+ end
612
+
613
+ def defined?
614
+ @defined
615
+ end
616
+
617
+ protected
618
+ def define!
619
+ @defined = true
620
+ end
621
+
622
+
623
+ # :call-seq:
624
+ # base_dir = dir
625
+ #
626
+ # Sets the project's base directory. Allows you to specify a base directory by calling
627
+ # this accessor, or with the :base_dir property when calling #define.
628
+ #
629
+ # You can only set the base directory once for a given project, and only before accessing
630
+ # the base directory (for example, by calling #file or #path_to).
631
+ # Set the base directory. Note: you can only do this once for a project,
632
+ # and only before accessing the base directory. If you try reading the
633
+ # value with #base_dir, the base directory cannot be set again.
634
+ def base_dir=(dir)
635
+ raise 'Cannot set base directory twice, or after reading its value' if @base_dir
636
+ @base_dir = File.expand_path(dir)
637
+ end
638
+
639
+ # Sets the project layout. Accepts Layout object or class (or for that matter, anything
640
+ # that can expand).
641
+ def layout=(layout)
642
+ raise 'Cannot set directory layout twice, or after reading its value' if @layout
643
+ @layout = layout.is_a?(Class) ? layout.new : layout
644
+ end
645
+
646
+ # :call-seq:
647
+ # define(name, properties?) { |project| ... } => project
648
+ #
649
+ # Define a new sub-project within this project. See Buildr#define.
650
+ def define(name, properties = nil, &block)
651
+ Project.define "#{self.name}:#{name}", properties, &block
652
+ end
653
+
654
+ def execute(args) #:nodoc:
655
+ Buildr.application.switch_to_namespace name.split(':') do
656
+ super
657
+ end
658
+ end
659
+
660
+ # Call all extension callbacks for a particular phase, e.g. :before_define, :after_define.
661
+ def call_callbacks(phase) #:nodoc:
662
+ remaining = @callbacks.select { |cb| cb.phase == phase }
663
+ known_callbacks = remaining.map { |cb| cb.name }
664
+
665
+ # call each extension in order
666
+ until remaining.empty?
667
+ callback = first_satisfied(remaining, known_callbacks)
668
+ if callback.nil?
669
+ hash = remaining.map { |cb| { cb.name => cb.dependencies} }
670
+ fail "Unsatisfied dependencies in extensions for #{phase}: #{hash.inspect}"
671
+ end
672
+ callback.blocks.each { |b| b.call(self) }
673
+ end
674
+ end
675
+
676
+ private
677
+
678
+ # find first callback with satisfied dependencies
679
+ def first_satisfied(r, known_callbacks)
680
+ remaining_names = r.map { |cb| cb.name }
681
+ res = r.find do |cb|
682
+ cb.dependencies.each do |dep|
683
+ fail "Unknown #{phase.inspect} extension dependency: #{dep.inspect}" unless known_callbacks.index(dep)
684
+ end
685
+ satisfied = cb.dependencies.find { |dep| remaining_names.index(dep) } == nil
686
+ cb if satisfied
687
+ end
688
+ r.delete res
689
+ end
690
+
691
+ end
692
+
693
+
694
+ # The basic mechanism for extending projects in Buildr are Ruby modules. In fact,
695
+ # base features like compiling and testing are all developed in the form of modules,
696
+ # and then added to the core Project class.
697
+ #
698
+ # A module defines instance methods that are then mixed into the project and become
699
+ # instance methods of the project. There are two general ways for extending projects.
700
+ # You can extend all projects by including the module in Project:
701
+ # class Project
702
+ # include MyExtension
703
+ # end
704
+ # You can also extend a given project instance and only that instance by extending
705
+ # it with the module:
706
+ # define 'foo' do
707
+ # extend MyExtension
708
+ # end
709
+ #
710
+ # Some extensions require tighter integration with the project, specifically for
711
+ # setting up tasks and properties, or for configuring tasks based on the project
712
+ # definition. You can do that by adding callbacks to the process.
713
+ #
714
+ # The easiest way to add callbacks is by incorporating the Extension module in your
715
+ # own extension, and using the various class methods to define callback behavior:
716
+ # * first_time -- This block will be called once for any particular extension.
717
+ # You can use this to setup top-level and local tasks.
718
+ # * before_define -- This block is called once for the project with the project
719
+ # instance, right before running the project definition. You can use this
720
+ # to add tasks and set properties that will be used in the project definition.
721
+ # * after_define -- This block is called once for the project with the project
722
+ # instance, right after running the project definition. You can use this to
723
+ # do any post-processing that depends on the project definition.
724
+ #
725
+ # This example illustrates how to write a simple extension:
726
+ # module LinesOfCode
727
+ # include Extension
728
+ #
729
+ # first_time do
730
+ # # Define task not specific to any projet.
731
+ # desc 'Count lines of code in current project'
732
+ # Project.local_task('loc')
733
+ # end
734
+ #
735
+ # before_define do |project|
736
+ # # Define the loc task for this particular project.
737
+ # Rake::Task.define_task 'loc' do |task|
738
+ # lines = task.prerequisites.map { |path| Dir['#{path}/**/*'] }.flatten.uniq.
739
+ # inject(0) { |total, file| total + File.readlines(file).count }
740
+ # puts "Project #{project.name} has #{lines} lines of code"
741
+ # end
742
+ # end
743
+ #
744
+ # after_define do |project|
745
+ # # Now that we know all the source directories, add them.
746
+ # task('loc'=>compile.sources + compile.test.sources)
747
+ # end
748
+ #
749
+ # # To use this method in your project:
750
+ # # loc path_1, path_2
751
+ # def loc(*paths)
752
+ # task('loc'=>paths)
753
+ # end
754
+ #
755
+ # end
756
+ #
757
+ # class Buildr::Project
758
+ # include LinesOfCode
759
+ # end
760
+ module Extension
761
+
762
+ # Extension callback details
763
+ class Callback #:nodoc:
764
+ attr_accessor :phase, :name, :dependencies, :blocks
765
+
766
+ def initialize(phase, name, dependencies, blocks)
767
+ @phase = phase
768
+ @name = name
769
+ @dependencies = dependencies
770
+ @blocks = (blocks ? (Array === blocks ? blocks : [blocks]) : [])
771
+ end
772
+
773
+ def merge(callback)
774
+ Callback.new(phase, name, @dependencies + callback.dependencies, @blocks + callback.blocks)
775
+ end
776
+ end
777
+
778
+ def self.included(base) #:nodoc:
779
+ base.extend ClassMethods
780
+ end
781
+
782
+ # Methods added to the extension module when including Extension.
783
+ module ClassMethods
784
+
785
+ def included(base) #:nodoc:
786
+ # When included in Project, add module instance, merge callbacks and call first_time.
787
+ if Project == base && !base.extension_modules.include?(module_callbacks)
788
+ base.extension_modules << module_callbacks
789
+ merge_callbacks(base.global_callbacks, module_callbacks)
790
+ first_time = module_callbacks.select { |c| c.phase == :first_time }
791
+ first_time.each do |c|
792
+ c.blocks.each { |b| b.call }
793
+ end
794
+ end
795
+ end
796
+
797
+ def extended(base) #:nodoc:
798
+ # When extending project, merge after_define callbacks and call before_define callback(s)
799
+ # immediately
800
+ if Project === base
801
+ merge_callbacks(base.callbacks, module_callbacks.select { |cb| cb.phase == :after_define })
802
+ calls = module_callbacks.select { |cb| cb.phase == :before_define }
803
+ calls.each do |cb|
804
+ cb.blocks.each { |b| b.call(base) } unless base.calledback[cb]
805
+ base.calledback[cb] = cb
806
+ end
807
+ end
808
+ end
809
+
810
+ # This block will be called once for any particular extension included in Project.
811
+ # You can use this to setup top-level and local tasks.
812
+ def first_time(&block)
813
+ module_callbacks << Callback.new(:first_time, self.name, [], block)
814
+ end
815
+
816
+ # This block is called once for the project with the project instance,
817
+ # right before running the project definition. You can use this to add
818
+ # tasks and set properties that will be used in the project definition.
819
+ #
820
+ # The block may be named and dependencies may be declared similar to Rake
821
+ # task dependencies:
822
+ #
823
+ # before_define(:my_setup) do |project|
824
+ # # do stuff on project
825
+ # end
826
+ #
827
+ # # my_setup code must run before :compile
828
+ # before_define(:compile => :my_setup)
829
+ #
830
+ def before_define(*args, &block)
831
+ if args.empty?
832
+ name = self.name
833
+ deps = []
834
+ else
835
+ name, args, deps = Buildr.application.resolve_args(args)
836
+ end
837
+ module_callbacks << Callback.new(:before_define, name, deps, block)
838
+ end
839
+
840
+ # This block is called once for the project with the project instance,
841
+ # right after running the project definition. You can use this to do
842
+ # any post-processing that depends on the project definition.
843
+ #
844
+ # The block may be named and dependencies may be declared similar to Rake
845
+ # task dependencies:
846
+ #
847
+ # after_define(:my_setup) do |project|
848
+ # # do stuff on project
849
+ # end
850
+ #
851
+ # # my_setup code must run before :compile (but only after project is defined)
852
+ # after_define(:compile => :my_setup)
853
+ #
854
+ def after_define(*args, &block)
855
+ if args.empty?
856
+ name = self.name
857
+ deps = []
858
+ else
859
+ name, args, deps = Buildr.application.resolve_args(args)
860
+ end
861
+ module_callbacks << Callback.new(:after_define, name, deps, block)
862
+ end
863
+
864
+ private
865
+
866
+ def module_callbacks
867
+ begin
868
+ const_get('Callbacks')
869
+ rescue
870
+ callbacks = []
871
+ const_set('Callbacks', callbacks)
872
+ end
873
+ end
874
+
875
+ def merge_callbacks(base, merge)
876
+ # index by phase and name
877
+ index = base.inject({}) { |hash,cb| { [cb.phase, cb.name] => cb } }
878
+ merge.each do |cb|
879
+ existing = index[[cb.phase, cb.name]]
880
+ if existing
881
+ base[base.index(existing)] = existing.merge(cb)
882
+ else
883
+ base << cb
884
+ end
885
+ index[[cb.phase, cb.name]] = cb
886
+ end
887
+ base
888
+ end
889
+ end
890
+
891
+ end
892
+
893
+
894
+ # :call-seq:
895
+ # define(name, properties?) { |project| ... } => project
896
+ #
897
+ # Defines a new project.
898
+ #
899
+ # The first argument is the project name. Each project must have a unique name.
900
+ # For a sub-project, the actual project name is created by prefixing the parent
901
+ # project's name.
902
+ #
903
+ # The second argument is optional and contains a hash or properties that are set
904
+ # on the project. You can only use properties that are supported by the project
905
+ # definition, e.g. :group and :version. You can also set these properties from the
906
+ # project definition.
907
+ #
908
+ # You pass a block that is executed in the context of the project definition.
909
+ # This block is used to define the project and tasks that are part of the project.
910
+ # Do not perform any work inside the project itself, as it will execute each time
911
+ # the Buildfile is loaded. Instead, use it to create and extend tasks that are
912
+ # related to the project.
913
+ #
914
+ # For example:
915
+ # define 'foo', :version=>'1.0' do
916
+ #
917
+ # define 'bar' do
918
+ # compile.with 'org.apache.axis2:axis2:jar:1.1'
919
+ # end
920
+ # end
921
+ #
922
+ # puts project('foo').version
923
+ # => '1.0'
924
+ # puts project('foo:bar').compile.classpath.map(&:to_spec)
925
+ # => 'org.apache.axis2:axis2:jar:1.1'
926
+ # % buildr build
927
+ # => Compiling 14 source files in foo:bar
928
+ def define(name, properties = nil, &block) #:yields:project
929
+ Project.define(name, properties, &block)
930
+ end
931
+
932
+ # :call-seq:
933
+ # project(name) => project
934
+ #
935
+ # Returns a project definition.
936
+ #
937
+ # When called from outside a project definition, must reference the project by its
938
+ # full name, e.g. 'foo:bar' to access the sub-project 'bar' in 'foo'. When called
939
+ # from inside a project, relative names are sufficient, e.g. <code>project('foo').project('bar')</code>
940
+ # will find the sub-project 'bar' in 'foo'.
941
+ #
942
+ # You cannot reference a project before the project is defined. When working with
943
+ # sub-projects, the project definition is stored by calling #define, and evaluated
944
+ # before a call to the parent project's #define method returns.
945
+ #
946
+ # However, if you call #project with the name of another sub-project, its definition
947
+ # is evaluated immediately. So the returned project definition is always complete,
948
+ # and you can access its definition (e.g. to find files relative to the base directory,
949
+ # or packages created by that project).
950
+ #
951
+ # For example:
952
+ # define 'myapp' do
953
+ # self.version = '1.1'
954
+ #
955
+ # define 'webapp' do
956
+ # # webapp is defined first, but beans is evaluated first
957
+ # compile.with project('beans')
958
+ # package :war
959
+ # end
960
+ #
961
+ # define 'beans' do
962
+ # package :jar
963
+ # end
964
+ # end
965
+ #
966
+ # puts project('myapp:beans').version
967
+ def project(*args, &block)
968
+ Project.project *args, &block
969
+ end
970
+
971
+ # :call-seq:
972
+ # projects(*names) => projects
973
+ #
974
+ # With no arguments, returns a list of all projects defined so far. When called on a project,
975
+ # returns all its sub-projects (direct descendants).
976
+ #
977
+ # With arguments, returns a list of named projects, fails on any name that does not exist.
978
+ # As with #project, you can use relative names when calling this method on a project.
979
+ #
980
+ # Like #project, this method evaluates the definition of each project before returning it.
981
+ # Be advised of circular dependencies.
982
+ #
983
+ # For example:
984
+ # files = projects.map { |prj| FileList[prj.path_to('src/**/*.java') }.flatten
985
+ # puts "There are #{files.size} source files in #{projects.size} projects"
986
+ #
987
+ # puts projects('myapp:beans', 'myapp:webapp').map(&:name)
988
+ # Same as:
989
+ # puts project('myapp').projects.map(&:name)
990
+ def projects(*args)
991
+ Project.projects *args
992
+ end
993
+
994
+ end